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.