Quick answer
json.loads() parses JSON text into a Python object. This error means you handed it something already parsed — a dict, list, or None. The fix is to not call json.loads at all and use the value directly, or to pass the raw text (e.g. response.text) instead of the parsed result.
The exact error string
import json
data = {"name": "Ada"} # already a dict
json.loads(data)
Traceback (most recent call last):
File "app.py", line 4, in <module>
json.loads(data)
TypeError: the JSON object must be str, bytes or bytearray, not dict
json.loads() accepts only str, bytes, or bytearray. Passing a Python object such as a dict, list, None, or a file object raises this TypeError.
json.loads ("load string") turns text into an object. If you already have the object, there is nothing to parse — calling it is the bug. The trailing word (dict, list, NoneType) tells you what you actually passed.
Cause 1: double-parsing a requests response
By far the most common case: wrapping response.json() — which already returns a dict — in another json.loads:
import requests
resp = requests.get("https://api.example.com/user")
# ❌ response.json() is already a dict
data = json.loads(resp.json()) # TypeError: ... not dict
# ✅ pick one:
data = resp.json() # parsed dict (recommended)
data = json.loads(resp.text) # parse the raw text yourself
Cause 2: the value is None
not NoneType means the value was None — commonly a dict.get() miss, an empty environment variable, or a function that returned nothing:
payload = config.get("body") # key missing -> None
json.loads(payload) # TypeError: ... not NoneType
# ✅ guard for None before parsing
if payload is not None:
data = json.loads(payload)
else:
data = {}
Cause 3: json.loads vs json.load on a file
Use json.load for an open file object and json.loads for a string. Mixing them is a frequent trigger:
# ❌ Wrong — f is a file object, not text
with open("data.json") as f:
data = json.loads(f)
# ✅ Option 1 — json.load reads AND parses the file object
with open("data.json") as f:
data = json.load(f)
# ✅ Option 2 — read to text first, then json.loads
with open("data.json") as f:
data = json.loads(f.read())
Cause 4: it is already a list
not list means the value is an already-parsed array. Don't re-parse it — iterate or index it directly:
rows = resp.json() # already a list of dicts
json.loads(rows) # ❌ TypeError: ... not list
for row in rows: # ✅ use it directly
print(row["id"])
Debugging checklist
- ✓ Print
type(value)— only calljson.loadswhen it isstrorbytes - ✓ Using requests? Use
response.json()orjson.loads(response.text), never both - ✓
not NoneType? The value isNone— guard before parsing - ✓ Reading a file?
json.load(f)for the file,json.loads(s)for a string - ✓
not list/not dict? It's already parsed — use it directly
Frequently Asked Questions
What does 'the JSON object must be str, bytes or bytearray, not dict' mean?
It means you passed json.loads() something that is already a Python object — a dict (or list, or None) — instead of JSON text. json.loads parses a string into an object; if you already have the object, you do not call json.loads at all. Use the value directly.
How do I fix it with requests?
Do not wrap response.json() in json.loads(). response.json() already returns a parsed dict, so json.loads(response.json()) passes a dict and fails. Either use response.json() alone, or json.loads(response.text) — never both.
Why does 'not list' or 'not NoneType' appear?
Same error, different value. not list means you passed an already-parsed array; not NoneType means the value was None — often because a .get() returned None or a function returned nothing. Trace where the value came from and pass JSON text, or skip the parse if it is already an object.
What is the difference between json.load and json.loads?
json.load(f) reads and parses from a file object; json.loads(s) parses from a string. Passing a file object to json.loads, or a dict to either, raises a TypeError. Use json.load for an open file and json.loads for a string you already read.
How do I check whether I even need json.loads?
Print type() of the value. If it is already dict or list, you are done — index it directly. Only call json.loads when type() is str or bytes. Calling json.loads on something already parsed is the whole cause of this error.
Is this the opposite of 'string indices must be integers'?
Effectively yes. string indices must be integers is indexing a string you forgot to parse; "the JSON object must be str... not dict" is parsing an object you already parsed. Both come from being unsure whether a value is text or a Python object — check its type once and the right call is obvious.
Inspect what your API actually returns
Paste the raw response into the formatter to confirm whether it's text to parse or already an object — nothing is uploaded to a server.