Common causes
1. The HTTP response body is empty
A 204 reply, a failed request or a server that crashed before writing returns an empty body, and response.json() fails at char 0. Check the status and the body before decoding.
resp = requests.get(url, timeout=10)
data = resp.json()resp = requests.get(url, timeout=10)
resp.raise_for_status()
data = resp.json() if resp.content else None2. The response is HTML or plain text, not JSON
Error pages from nginx, Cloudflare or an Envoy sidecar, login pages and rate-limit notices are not JSON. Check the content type and log the start of the body so the real problem is visible. See also Unexpected token ‘<’.
data = resp.json()if "application/json" not in resp.headers.get("Content-Type", ""):
raise RuntimeError(f"{resp.status_code} from {resp.url}: {resp.text[:200]!r}")
data = resp.json()3. json.loads given a file name instead of file contents
json.loads("config.json") tries to parse the eleven characters of the file name. Use json.load with an open file, or read the file first.
config = json.loads("config.json")with open("config.json", encoding="utf-8") as f:
config = json.load(f)4. The file was already read to the end
After f.read() the file position is at the end, so a following json.load(f) reads an empty string. Parse the text you already have, or f.seek(0) first.
with open("data.json", encoding="utf-8") as f:
print(f.read())
data = json.load(f)with open("data.json", encoding="utf-8") as f:
text = f.read()
print(text)
data = json.loads(text)5. A missing value inside the document
When the column is not 1, a value is missing or invalid at that spot: an empty slot after a colon, a trailing comma in an array, or a word like undefined or True.
{"order_id": 1182, "discount": , "currency": "EUR"}{"order_id": 1182, "discount": null, "currency": "EUR"}Frequently asked questions
What does "char 0" mean?
It is the zero-based character offset of the problem, so char 0 is the very first character. Combined with line 1 column 1 it almost always means the input is empty or is not JSON at all.
Why does requests raise requests.exceptions.JSONDecodeError?
Since requests 2.27, response.json() wraps the decoder error in its own JSONDecodeError class, which is also a subclass of the standard one. The message and the cause are the same.
How do I see what Python actually received?
Print repr(resp.text[:500]) or repr(text[:500]) before decoding. repr shows an empty string as ‘’ and makes invisible characters such as a BOM or stray whitespace visible.