Expecting value: line 1 column 1 (char 0)
Python is telling you that the very first character of what you handed it is not the start of a JSON value. In practice this almost never means your JSON is subtly malformed. It means what you parsed is not JSON: an HTML error page, an empty body, a plain-text message, or nothing at all. The position is line 1 column 1 because the parser failed before it read anything.
You may also see this written as
- json.decoder.JSONDecodeError: Expecting value
V8 rewrote most of its JSON error messages in 2022. Older wording is still what most search results show, but no current runtime emits it.
Paste your JSON and see exactly where it breaks
Nothing you paste leaves your browser. The connect-src allowlist makes that a browser guarantee rather than a promise. Check it yourself
What actually causes it
Ranked by how often each one turns out to be the answer.
-
01 The response body is HTML, not JSON
A 404 page, a login redirect, a Cloudflare challenge or a proxy error page. The request succeeded at the HTTP level and returned a document, just not the one you expected. Print the first few hundred characters before parsing and you will see it immediately.
Breaks
r = requests.get(url) data = r.json() # JSONDecodeErrorWorks
r = requests.get(url) if r.status_code != 200 or 'json' not in r.headers.get('content-type', ''): raise RuntimeError(f"{r.status_code} {r.headers.get('content-type')}: {r.text[:300]}") data = r.json() -
02 The body is empty
An HTTP 204, a rate-limited request that returned nothing, a WAF that dropped the body, or a file that exists but has zero bytes. An empty string is not valid JSON. The shortest valid documents are null, 0, "", [] and {}.
Breaks
json.loads("")Works
text = r.text if not text.strip(): return None data = json.loads(text) -
03 You are reading a file that is not JSON
A CSV, a log file, a Python pickle, or a JSON Lines file where every line is a separate document. For JSON Lines, parse line by line rather than all at once.
Breaks
with open('events.jsonl') as f: data = json.load(f) # Expecting value, or Extra dataWorks
with open('events.jsonl') as f: data = [json.loads(line) for line in f if line.strip()] -
04 Authentication failed and the API returned a plain-text message
Many APIs return "Unauthorized" or "Forbidden" as plain text with a 401 or 403, not as JSON. Check the status code before you check the body.
The same mistake in other runtimes
The underlying problem is identical; only the wording differs. If a colleague reports one of these, they are looking at the same thing you are.
| JavaScript (V8) | Unexpected token '<', "<!DOCTYPE "... is not valid JSON |
|---|---|
| JavaScript (V8), empty input | Unexpected end of JSON input |
| C# (Newtonsoft) | Unexpected character encountered while parsing value: <. Path '', line 0, position 0. |
Questions
- Why does it always say line 1 column 1?
- Because the parser failed on the very first character. It never got far enough to reach line 2. If your error names a later line and column, the problem is genuinely inside your JSON and this is a different error.
- How do I see what I actually received?
- Print response.text[:500] before calling .json(). In nine cases out of ten the answer is visible in the first line: a <!DOCTYPE html>, an empty string, or a short plain-text message.
- Does json.loads accept a bytes object?
- Yes, since Python 3.6 json.loads accepts bytes and bytearray as well as str, and detects UTF-8, UTF-16 and UTF-32. So this error is not caused by passing bytes.
Fix it now
Paste the payload into the tool above, or go straight to the one built for this job.
Validate your JSON