Skip to content

gh-160309: Fix StreamReader.read() silently dropping an incomplete final character - #160642

Open
saiyam-jain-dev wants to merge 2 commits into
python:mainfrom
saiyam-jain-dev:gh-160309-codecs-streamreader-truncation
Open

saiyam-jain-dev wants to merge 2 commits into
python:mainfrom
saiyam-jain-dev:gh-160309-codecs-streamreader-truncation

Conversation

@saiyam-jain-dev

@saiyam-jain-dev saiyam-jain-dev commented Oct 25, 2026 •

Copy link
Copy Markdown

Summary

Fixes #160309

codecs.StreamReader.read() silently drops bytes when the stream ends with an incomplete multibyte sequence. The read loop exits on empty
ewdata while �ytebuffer still holds the trailing bytes, and those bytes are never decoded.

Bug demonstration (before fix)

`python

import codecs, io
reader = codecs.getreader('utf-8')(io.BytesIO(b'ab\xe2'))
reader.read()
'ab' # \xe2 is silently dropped — no error raised
`

Every other decoding path raises or applies the error handler:

`python

b'ab\xe2'.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe2 in position 2: unexpected end of data
codecs.getreader('utf-8')(io.BytesIO(b'ab\xe2'), 'replace').read()
'ab' # should be 'ab\ufffd' — replacement character is also silently dropped
`

Fix

When the underlying stream is exhausted and �ytebuffer is non-empty, decode the leftover bytes immediately before breaking out of the loop. The codec's stateless decode function applies inal=True semantics, so:

  • errors='strict' → raises UnicodeDecodeError (correct)
  • errors='replace' → emits the replacement character \ufffd (correct)
  • errors='ignore' → drops the bytes (correct)

This matches the behaviour of �ytes.decode() and io.TextIOWrapper.

@python-cla-bot

python-cla-bot Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@bedevere-app

bedevere-app Bot commented Oct 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@BHUVANSH855 BHUVANSH855 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLA check is failing, sign it we can't review without that.

@saiyam-jain-dev
saiyam-jain-dev force-pushed the gh-160309-codecs-streamreader-truncation branch from 29f7951 to db97211 Compare October 25, 2026 20:54
@bedevere-app

bedevere-app Bot commented Oct 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@saiyam-jain-dev

Copy link
Copy Markdown
Author

The commit author email has been updated to match the signed CLA, and the CLA check is now passing (green ✅). I have also ensured inal=True semantics are applied during EOF decode and added regression test coverage in est_codecs.py.

… final character

When a byte stream ends with an incomplete multibyte sequence (e.g. a
truncated UTF-8 character), StreamReader.read() would silently discard
those bytes: the read loop exited on empty newdata while bytebuffer still
held the fragment, and bytebuffer was never decoded.

Fix by detecting leftover bytes in bytebuffer when the underlying stream
is exhausted and decoding them immediately.  The codec's stateless decode
function (e.g. utf_8_decode with final=True) then applies the error
handler: 'strict' raises UnicodeDecodeError, 'replace' emits the
replacement character, and 'ignore' drops the bytes - matching the
behaviour of bytes.decode() and io.TextIOWrapper.
@saiyam-jain-dev
saiyam-jain-dev force-pushed the gh-160309-codecs-streamreader-truncation branch from db97211 to e6fb522 Compare October 25, 2026 21:14
@bedevere-app

bedevere-app Bot commented Oct 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

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.

codecs.StreamReader.read() silently drops an incomplete character at the end of the stream

2 participants