Python JSON Reference

The JSON object must be str, bytes or bytearray — what it means and how to fix it

This is a Python TypeError, not a JSON formatting problem. It happens when json.loads() receives something that isn't already a string, bytes, or bytearray — most commonly because the value passed in is already a Python object (a dict, list, file handle, or HTTP response object) rather than raw JSON text.

TypeError: the JSON object must be str, bytes or bytearray, not dict

Below are the four most common variants of this error and how to fix each one.

1"not dict" or "not list"

This happens when you already have a native Python dict or list and mistakenly call json.loads() on it. The two main functions go in opposite directions: json.dumps() converts a Python object to a JSON string, while json.loads() parses a JSON string back into a Python object. If you already have the object, you don't need to parse it — and if you need JSON text, you need dumps(), not loads().

Wrong — loads() on a dict
import json

data = {"name": "Alice", "age": 30}
text = json.loads(data)
# TypeError: the JSON object must be
# str, bytes or bytearray, not dict
Correct — use dumps() instead
import json

data = {"name": "Alice", "age": 30}
text = json.dumps(data)
# '{"name": "Alice", "age": 30}'

2"not TextIOWrapper"

This happens when you pass an open file object directly to json.loads() instead of json.load() (no s). The difference: json.load() reads directly from a file-like object, while json.loads() expects a string that has already been read into memory. Either switch to json.load(f), or read the file first with f.read().

Wrong — loads() on a file object
import json

with open("config.json") as f:
    data = json.loads(f)
# TypeError: the JSON object must be
# str, bytes or bytearray, not
# TextIOWrapper
Correct — use load() (no s)
import json

with open("config.json") as f:
    data = json.load(f)
# Reads and parses in one step

3"not Response"

This is extremely common when using the requests library. Calling json.loads() on a Response object passes the entire object — not its body text. The requests library already provides a built-in .json() method that handles decoding for you. Alternatively, you can pass response.text to json.loads().

Wrong — loads() on Response
import json, requests

resp = requests.get("https://api.example.com/data")
data = json.loads(resp)
# TypeError: the JSON object must be
# str, bytes or bytearray, not Response
Correct — use .json()
import requests

resp = requests.get("https://api.example.com/data")
data = resp.json()
# Built-in method, no import json needed

4"not coroutine"

In async code (e.g. aiohttp, httpx, or FastAPI), forgetting to await an async function call means you get a coroutine object instead of the actual result. Passing that coroutine to json.loads() triggers this TypeError. The fix is to await the async call first so you receive the resolved string value.

Wrong — missing await
import json, aiohttp

async def fetch():
    async with aiohttp.ClientSession() as s:
        resp = await s.get("https://api.example.com")
        body = resp.text()   # missing await!
        data = json.loads(body)
        # TypeError: ... not coroutine
Correct — await first
import json, aiohttp

async def fetch():
    async with aiohttp.ClientSession() as s:
        resp = await s.get("https://api.example.com")
        body = await resp.text()
        data = json.loads(body)
Getting a syntax error instead of a TypeError? You may have a trailing comma in your JSON.

Already have a JSON string? Validate it instantly.

Paste your JSON into our formatter to check for syntax errors, pretty-print, or minify — all in your browser.

Open the free JSON Formatter