json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Python · updated 2026-11-02

Raised by json.loads, and by requests Response.json() (as requests.exceptions.JSONDecodeError, a subclass), when the very first character is not valid JSON. "line 1 column 1 (char 0)" means nothing parseable came back at all: an empty body, an HTML error page, a plain-text message or a redirect page.

Traceback (most recent call last):
  File "/srv/app/sync.py", line 22, in fetch_items
    items = resp.json()["items"]
            ^^^^^^^^^^^
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Quick fix

Check the response before parsing it: log resp.status_code, the Content-Type header and the first 200 characters of resp.text. Call resp.raise_for_status() first so 4xx and 5xx error pages raise an HTTP error with the status instead of a JSON error, and treat 204 or an empty body as "no data".

  1. Log status_code, headers["Content-Type"] and resp.text[:200] for the failing call.
  2. Call resp.raise_for_status() before resp.json().
  3. Handle 204 No Content and empty bodies explicitly.
  4. If the body is HTML, look for a login page, a proxy or rate-limit page, or a wrong URL.
  5. For files, check the file is not empty or still being written when it is read.
python
resp = session.get(url, timeout=10)
resp.raise_for_status()                      # 4xx/5xx raise HTTPError with the status
if resp.status_code == 204 or not resp.content:
    return []
if "json" not in resp.headers.get("Content-Type", ""):
    raise ValueError(f"expected JSON from {url}, got {resp.headers.get('Content-Type')}: {resp.text[:200]!r}")
items = resp.json()["items"]

Why it happens

An error page instead of JSON

A 500, 502 or 404 from a server, load balancer or CDN returns HTML. Parsing it fails on the first character, "<".

An empty body

A 204 No Content, a timeout cut short, or an endpoint that returns nothing on success leaves an empty string, which is not valid JSON.

Authentication or redirects

An expired token can lead to a login page or a redirect, and the HTML of that page is what gets parsed.

Rate limiting or a proxy

Rate-limit responses and corporate proxies often answer in plain text or HTML.

Reading a file that is empty or not JSON

json.load on a file still being written, truncated, or saved with a byte-order mark fails the same way.

Other fixes

  • Wrap external API calls in one helper that checks status and content type before parsing.
  • Record the status code and a short body excerpt with the error so the cause is visible in your error tracker.
  • Read files that may carry a BOM with encoding="utf-8-sig".

Same error, other wording

  • json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  • requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  • json.decoder.JSONDecodeError: Extra data: line 1 column 5 (char 4)

Let your coding agent catch the next one

Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:

> Find the JSONDecodeError "Expecting value" errors in production, show which URL or file each came from and what the response was (status, content type, first bytes), and handle that case at the call site.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

References: Python: json.JSONDecodeError, Requests: Response.json() and raise_for_status()