Skip to content

gh-159071: Fix utf-8-sig decode error offsets - #159082

Closed
hetaozdh wants to merge 1 commit into
python:mainfrom
hetaozdh:gh-159071-utf8sig-bom-error-offsets
Closed

hetaozdh wants to merge 1 commit into
python:mainfrom
hetaozdh:gh-159071-utf8sig-bom-error-offsets

Conversation

@hetaozdh

@hetaozdh hetaozdh commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #159071.

Problem

utf-8-sig strips the BOM before decoding. If exception, the resulting UnicodeDecodeError refers to the stripped input: start and end are both small by 3, and object no longer contains the BOM.

For example, b'\xef\xbb\xbf\xff'.decode('utf-8-sig') reports position 0, while the invalid byte is at position 3. The utf-16 and utf-32 codecs already report offsets relative to the original input.

Fix

Add _fix_decode_error() to restore the original input and correct the offsets. It was used in decode(), IncrementalDecoder._buffer_decode() and StreamReader.decode().

The exception is adjusted in place to preserve its traceback. Incremental decoding uses the buffered input as the base, so offsets remain correct even when the BOM is split across chunks. The helper also handles memoryview inputs, converting them to bytes when needed.

Tests

Add tests for the codec API, incremental decoding and stream readers, including invalid bytes, truncated sequences and a BOM split across chunks. All three tests fail without the fix, and test_codecs passes with it.

Notes

Custom error handlers still see offsets relative to the BOM-stripped input, since they run before the exception reaches the caller. Fixing that would require wrapping the handler and is outside this PR's scope.

No documentation changes are needed.

AI tools were used in preparing this change.

/cc @alicederyn Thanks to Alice.

The utf-8-sig codec strips the BOM before decoding, causing
UnicodeDecodeError offsets and its object to refer to the
BOM-stripped input rather than the original input.

Adjust the exception in decode(), IncrementalDecoder._buffer_decode()
and StreamReader.decode() to restore the original input and offsets.

Custom error handlers still see offsets relative to the
BOM-stripped input.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

utf-8-sig: UnicodeDecodeError offsets do not count the BOM

1 participant