Technology · PRACTICAL GUIDE
Python UnicodeDecodeError: invalid start byte? Preserve the bytes before choosing an encoding
The exception names the decoder that failed, not the encoding you should guess. Preserve the input, classify text versus binary, prove the producer contract and validate one strict decode before any lossy fallback.

An invalid start byte is a failed decoding attempt, not an automatic instruction to try latin-1 or ignore errors. Keep the original bytes and determine what produced them before changing codecs or data.
Freeze the exact bytes and failure context
Copy or hash the original before opening it in an editor. Read a small sample in binary mode and record the source command, file size, first bytes, failing byte offset, Python version and the exact codec name. A text editor, spreadsheet importer or shell pipeline may already have transformed line endings or replaced bytes, so diagnose the earliest unchanged copy.
UnicodeDecodeError reports that a selected decoder rejected a byte sequence. It is not an encoding detector. The same byte can be invalid UTF-8, valid in another documented encoding, part of a ZIP or image header, or evidence that a transfer was truncated.
Decide whether the payload is text
Check the producer and container before trying codecs. Magic bytes, a documented archive format, compressed output, an XLSX package, an image or a serialized binary protocol needs its matching parser first. Renaming the file or decoding container bytes as text cannot reveal the embedded records.
If the producer promises text, obtain the documented encoding from the protocol, export option, HTTP header, database driver or command documentation. A BOM can be supporting evidence for some Unicode encodings, but its absence does not identify an encoding and its presence should be handled by the appropriate codec.
Test one evidence-backed codec strictly
Decode a preserved byte sample and then the complete stream with the one codec supported by the producer contract. Keep errors=strict while establishing correctness. A successful decode is necessary but not sufficient: validate delimiters, record counts, required fields and application invariants because permissive single-byte codecs can map every byte without proving that the text is right.
If the strict decode fails, compare the failing offset with the raw bytes and upstream boundaries. Re-fetch or re-export if the file ends mid-sequence, mixes encodings, contains a binary segment or was concatenated from different producers. Do not move the goalposts by cycling through codecs until one stops raising.
Use error handlers only for a named purpose
errors=ignore discards undecodable data and errors=replace inserts a replacement character; both change the content. They can produce a readable preview when loss is explicitly acceptable, but they are not proof of a repaired source. Save the original bytes, count every affected span and keep the lossy output separate.
backslashreplace can make bad bytes visible in diagnostics. surrogateescape is useful in limited round-trip workflows because undecodable bytes can be represented in a Python string and encoded back with the same handler. Neither establishes the intended characters; document the boundary and never pass surrogate data to systems that require valid Unicode.
Close with a verifiable conversion
A successful repair names the producer, container, encoding and parser; strictly decodes the complete input; validates the resulting records; and writes a new output without overwriting the evidence. Record hashes, counts and any quarantined byte ranges so another reviewer can reproduce the decision.
Stop and escalate when the source contract is unknown, the same file mixes encodings, a binary boundary cannot be reconstructed or replacement would alter identifiers, money, measurements or audit fields. Preserving uncertainty is safer than publishing plausible but invented characters.
