AttributeError: 'str' object has no attribute 'decode' (tested fix)
AttributeError: 'str' object has no attribute 'decode' means you called .decode() on something that is already text. In Python 3 only bytes can be decoded. We checked which common calls return str and which return bytes, and wrote a helper for values that can be either.
1. Which values are str and which are bytes
In Python 3 only bytes (and bytearray) have .decode(). A str is already text. The error means the value was already decoded, usually by a library that returns str where your code (often written for Python 2) expected bytes. We checked the usual sources:
import base64, json, subprocess
def show(label, value):
try:
print(f"{label:38} {type(value).__name__:5} -> .decode(): {value.decode()!r}")
except AttributeError as e:
print(f"{label:38} {type(value).__name__:5} -> AttributeError: {e}")
show("'café' (str literal)", "café")
show("'café'.encode()", "café".encode())
show("base64.b64encode(b'hi')", base64.b64encode(b"hi"))
show("json.dumps({'a': 1})", json.dumps({"a": 1}))
show("subprocess.check_output(['echo','x'])", subprocess.check_output(["echo", "x"]))
show("subprocess ... text=True", subprocess.check_output(["echo", "x"], text=True))
show("open(..., 'rb').read(5)", open(__file__, "rb").read(5))
show("open(...).read(5)", open(__file__).read(5))
Output:
'café' (str literal) str -> AttributeError: 'str' object has no attribute 'decode'
'café'.encode() bytes -> .decode(): 'café'
base64.b64encode(b'hi') bytes -> .decode(): 'aGk='
json.dumps({'a': 1}) str -> AttributeError: 'str' object has no attribute 'decode'
subprocess.check_output(['echo','x']) bytes -> .decode(): 'x\n'
subprocess ... text=True str -> AttributeError: 'str' object has no attribute 'decode'
open(..., 'rb').read(5) bytes -> .decode(): 'impor'
open(...).read(5) str -> AttributeError: 'str' object has no attribute 'decode'
The quick fix is to delete the .decode() call where the value is already a str: json.dumps(), text-mode files, subprocess with text=True, and most library return values. Keep it only for genuine bytes: binary-mode files, sockets, base64 output and subprocess without text=True.
2. When the value can be either: decode only bytes
If a function receives str from some callers and bytes from others (common when a library changed its return type between versions), check the type instead of guessing:
def to_text(value, encoding="utf-8"):
"""Return str whether you were given str or bytes."""
if isinstance(value, (bytes, bytearray)):
return value.decode(encoding)
if isinstance(value, str):
return value
raise TypeError(f"expected str or bytes, got {type(value).__name__}")
for v in [b"caf\xc3\xa9", "café", bytearray(b"ok")]:
print(repr(v), "->", repr(to_text(v)))
# the Python 2 habit that no longer works
s = "caf\xc3\xa9" # mojibake: UTF-8 bytes stored in a str
print(repr(s), "->", repr(s.encode("latin-1").decode("utf-8")))
try:
"x".encode().encode()
except AttributeError as e:
print("AttributeError:", e)
Output:
b'caf\xc3\xa9' -> 'café'
'café' -> 'café'
bytearray(b'ok') -> 'ok'
'café' -> 'café'
AttributeError: 'bytes' object has no attribute 'encode'
The mirror error, 'bytes' object has no attribute 'encode', is the same mistake in the other direction. Don't "fix" either error with str(value): str(b"x") gives the string "b'x'", with the b and quotes inside it. If the text came out as café, it was decoded with the wrong codec, and .encode("latin-1").decode("utf-8") reverses that, as shown above. For decode failures see our UnicodeDecodeError guide.
Frequently asked questions
How do I fix 'str' object has no attribute 'decode'?
Remove the .decode() call: the value is already a str. If it can be str or bytes, decode only when isinstance(value, (bytes, bytearray)).
Why did this work in Python 2?
Python 2 str was a byte string with .decode(). In Python 3, str is Unicode text and has no .decode(); only bytes does.
Does json.dumps return bytes?
No. json.dumps returns str, so json.dumps(obj).decode() raises AttributeError. Use .encode() if you need bytes.
Does subprocess.check_output return str or bytes?
bytes by default; str when you pass text=True (or encoding=...). Call .decode() only in the bytes case.
Written with AI assistance. Measured on 2026-10-22 on Debian with Python 3.13.5: str literals, encoded bytes, base64.b64encode, json.dumps, subprocess.check_output with and without text=True, and binary vs text file reads. Every block was run verbatim and its output pasted unedited. PlainHeaders is our extension; Python is free.