From ed495deb650d9d338e94e0cede30094d6e0fcbf5 Mon Sep 17 00:00:00 2001 From: Tabish Mustufa Date: Sat, 5 Sep 2026 21:42:36 -0700 Subject: [PATCH] Raise FitError for an empty FIT file A zero-byte file was parsed as an empty-but-successful activity instead of an error, indistinguishable from a truncated download. Co-Authored-By: Claude Sonnet 5 --- src/activity_parser/parse_fit.py | 3 +++ tests/test_parse_fit.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/activity_parser/parse_fit.py b/src/activity_parser/parse_fit.py index bcb52c7..bc1cf14 100644 --- a/src/activity_parser/parse_fit.py +++ b/src/activity_parser/parse_fit.py @@ -189,6 +189,9 @@ def decode_fit( except (gzip.BadGzipFile, zlib.error, EOFError) as e: raise FitError(str(e)) from e + if not data: + raise FitError("Empty FIT file") + messages, errors = Decoder(Stream.from_byte_array(data)).read( enable_crc_check=check_crc, merge_heart_rates=merge_heart_rates ) diff --git a/tests/test_parse_fit.py b/tests/test_parse_fit.py index 181f053..cc82c58 100644 --- a/tests/test_parse_fit.py +++ b/tests/test_parse_fit.py @@ -335,6 +335,18 @@ def test_non_fit_bytes_raise(): parse_fit(io.BytesIO(b"this is not a FIT file")) +@pytest.mark.parametrize("parser", [parse_fit, parse_fit_raw]) +def test_zero_byte_file_raises(parser): + with pytest.raises(FitError, match="[Ee]mpty"): + parser(io.BytesIO(b"")) + + +@pytest.mark.parametrize("parser", [parse_fit, parse_fit_raw]) +def test_short_garbage_stub_raises(parser): + with pytest.raises(FitError, match="not a fit file"): + parser(io.BytesIO(b"\x00" * 13)) + + @pytest.mark.parametrize( "corrupt_bytes", [