UnicodeDecodeError: 'utf-8' codec can't decode

Python tried to read bytes as UTF-8 and hit a byte sequence that is not valid UTF-8. The file (or socket) is almost certainly another encoding — or it is binary.

What the traceback is saying

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 42: invalid continuation byte
# or: invalid start byte / unexpected end of data

Byte 0xe9 alone is fine in Latin-1/Windows-1252 (é) but incomplete as UTF-8 (UTF-8 would need a continuation byte). Python 3 defaults to UTF-8 for many text paths — including open() on modern Linux — so older files explode.

Usual causes

  1. CSV / log exported from Excel or a Windows app — often cp1252 (Windows-1252) or legacy cp850. Open with that encoding, or re-save as UTF-8 from the source app.
  2. Binary file opened as text — images, zip, protobuf, .pyc. Use open(path, 'rb') and do not decode.
  3. Mixed encodings in one stream — concatenated logs from different machines. Decode line-by-line with a fallback, or normalise upstream.
  4. HTTP / subprocess output — declared charset lies, or none given. Prefer bytes first: resp.content, then decode using the header or chardet/charset-normalizer.
  5. Git showed mojibake earlier — if the file already looks like é instead of é, see mojibake; decoding as UTF-8 may “succeed” into nonsense, which is a different failure mode.

Fixes that work

# 1) Know the encoding (Excel Western Europe → cp1252)
open('export.csv', encoding='cp1252')
# latin-1 never raises (maps every byte); good for forensic reads, not always correct semantics
open('export.csv', encoding='latin-1')

# 2) Binary
open('image.png', 'rb')

# 3) Sniff carefully (third-party)
# pip install charset-normalizer
from charset_normalizer import from_path
print(from_path('export.csv').best())

# 4) Explicit UTF-8 when you control the writer
open('out.txt', 'w', encoding='utf-8', newline='')

UTF-8 vs Windows-1252 cheat sheet

SymptomLikely story
UnicodeDecodeError on readBytes are not UTF-8; pick the real encoding
File opens but shows é / ’UTF-8 bytes misread as Latin-1/cp1252 (mojibake)
Emoji / Asian text breaks in MySQLColumn/connection not utf8mb4

Related

mojibake UTF-8 vs Windows-1252 · MySQL Incorrect string value utf8mb4 · LF will be replaced by CRLF · invisible zero-width characters · emoji shows as box / tofu

Questions

What does UnicodeDecodeError utf-8 codec can't decode mean?

Python tried to interpret bytes as UTF-8 and hit an invalid sequence. Open with the correct encoding or as binary.

Which encoding should I pick?

For old Windows/Excel CSVs try cp1252. For Western European text latin-1/ISO-8859-1 maps every byte. When unsure, sniff with charset-normalizer and verify a few rows.

Why did it work on my Mac but fail on Linux?

macOS locale or a library may have guessed differently; Linux Python 3 often defaults open() to UTF-8. Always pass encoding= explicitly in shared code.

Is errors='ignore' safe?

It drops bytes silently. Fine for rough log scraping; dangerous for data you will write back or bill against.

How is this different from mojibake?

UnicodeDecodeError refuses to decode. Mojibake means a wrong decoding already produced plausible-looking wrong characters. Related but opposite failure modes.

JSON / API responses throw this

Decode response bytes with the charset from Content-Type, or use the HTTP client’s .text helper. Do not assume UTF-8 if the server lied.

Published .

Try it

Related guides

Newest guides