Skip to content

TextIOWrapper.tell() returns a wrong position after next() and flush() #161055

Description

@gpshead

Bug report

Bug description:

TextIOWrapper.__next__() stops updating the decoder snapshot that tell() relies on and disables tell() (OSError: telling position disabled by next() call), but flush() re-enables tell() without restoring the snapshot. tell() then computes the position from a stale snapshot and silently returns a wrong cookie.

import io
f = io.TextIOWrapper(io.BytesIO(b"a\n" + b"b" * 10000 + b"\nc\n"),
                     encoding="ascii")
f.readline()
next(f)
f.flush()
pos = f.tell()
print(pos)
f.seek(pos)
print(repr(f.read(3)))

This prints 3624 and 'bbb' instead of 10003 and 'c\n' (or the OSError that tell() raises without the flush() call); _pyio.TextIOWrapper returns 9768, also wrong. If the snapshot was replaced by an earlier seek(), tell() instead raises OSError: can't reconstruct logical file position (before gh-161053 it read past the snapshot).

gh-81217 asks for next() not to disable tell() at all.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

  1. self-assigned this
    on Nov 1, 2026
  2. gpshead commented on Nov 1, 2026

    @gpshead
    MemberAuthor

    The repro still prints 3624 and 'bbb' on current main (9768 with _pyio). Calling next() as the first read, then flush() and tell(), is another way to trigger the bug.

    The PR makes flush() re-enable tell() only when no internal snapshot state or decoded text is pending, so in these cases tell() raises the same OSError as without the flush() call; seek() and reaching the end of iteration still re-enable it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions