io.TextIOWrapper.reconfigure() Mid-Write Encoding Switch Is Silent (tested)
io.TextIOWrapper.reconfigure() lets you change encoding, errors, or newline on an already-open text stream. On the read side, it enforces a safety rule: calling it after any read() has already happened raises UnsupportedOperation, because the internal decoder's state can't be swapped out mid-stream. The write side has no such restriction -- you can call reconfigure(encoding=...) after writing any amount of text, and it takes effect immediately for everything written afterward. Nothing re-encodes what was already written, so a single file can end up with two different byte encodings concatenated together, with no error or warning raised at write time.
1. Switching encoding mid-write succeeds with no error
import io
buf = io.BytesIO()
text = io.TextIOWrapper(buf, encoding="utf-8")
text.write("café ") # encoded as UTF-8
text.reconfigure(encoding="latin-1")
text.write("café") # encoded as Latin-1, same stream
text.flush()
print(buf.getvalue())
b'caf\xc3\xa9 caf\xe9'
Measured: the first "café " is encoded as UTF-8 (\xc3\xa9 for é), the second "café" right after it is encoded as Latin-1 (\xe9 for the same character) -- two different byte sequences for the same character, concatenated in one file, produced without any exception or warning.
2. The read side refuses the exact same thing
reader = io.TextIOWrapper(io.BytesIO(b"hello"), encoding="utf-8")
reader.read(1)
try:
reader.reconfigure(encoding="latin-1")
except Exception as e:
print("reconfigure after a read ->", type(e).__name__, e)
reconfigure after a read -> UnsupportedOperation It is not possible to set the encoding or newline of stream after the first read
Measured: reconfiguring the encoding on the read side raises immediately if even one character has already been read. The write side has no equivalent check -- the asymmetry is real, not an oversight you'd expect from symmetry between reading and writing the same class.
3. Reading the mixed-encoding file back goes wrong in two different ways
raw = b'caf\xc3\xa9 caf\xe9'
try:
io.TextIOWrapper(io.BytesIO(raw), encoding="utf-8").read()
except UnicodeDecodeError as e:
print("read back as utf-8 ->", e)
print("read back as latin-1 ->", repr(io.TextIOWrapper(io.BytesIO(raw), encoding="latin-1").read()))
read back as utf-8 -> 'utf-8' codec can't decode byte 0xe9 in position 9: unexpected end of data
read back as latin-1 -> 'café café'
Measured: reading the file back as UTF-8 (the encoding it started with) crashes with UnicodeDecodeError on the Latin-1-encoded tail. Reading it back as Latin-1 (the encoding it ended with) doesn't crash at all -- it silently decodes the UTF-8-encoded head as mojibake (café instead of café) with no error to flag that anything is wrong.
4. The fix: never reconfigure(encoding=...) on a stream you're actively writing
buf2 = io.BytesIO()
text2 = io.TextIOWrapper(buf2, encoding="utf-8")
text2.write("café ")
text2.write("café") # stay on the original encoding for the whole stream
text2.flush()
print(io.TextIOWrapper(io.BytesIO(buf2.getvalue()), encoding="utf-8").read())
café café
Measured: keeping a single TextIOWrapper on one encoding for its entire lifetime -- never calling reconfigure(encoding=...) mid-write -- round-trips correctly. If different parts of a file genuinely need different encodings, write them through separate streams and concatenate the raw bytes explicitly, so the encoding boundary is visible in the code instead of hidden inside one stream's state.
Frequently asked questions
Does TextIOWrapper.reconfigure(encoding=...) check anything before switching on the write side?
We measured no restriction at all on the write side -- it can be called after any number of writes and takes effect immediately for subsequent writes, with nothing re-encoding what was already written in the old encoding.
Why does the read side block reconfigure() but the write side doesn't?
We measured that reconfigure() raises UnsupportedOperation on the read side as soon as any read() has happened, because the internal incremental decoder has state that can't be swapped mid-stream. The write side doesn't have an equivalent check, even though re-encoding mid-write has the same kind of consequence -- a single stream ending up with inconsistent byte encodings.
How do I detect a file that was written with a mid-stream encoding switch?
There's no general way from the file alone -- we measured that reading it back with one encoding can either crash (if a later segment isn't valid in that encoding) or silently produce mojibake (if it happens to decode without error, just incorrectly) for the segment written in the other encoding.
Is this specific to switching between utf-8 and latin-1?
No -- we used those two because the difference is easy to see in the raw bytes, but the underlying issue is reconfigure(encoding=...)'s lack of a write-side guard, which applies to any pair of encodings, not just that specific combination.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5's built-in io.TextIOWrapper: wrote text, called reconfigure(encoding=...) to switch encodings mid-stream, wrote more text, then inspected the raw bytes and tried reading the result back with a single encoding. PlainHeaders is our extension; Python's io module is built in.