{}JSON FYI

ValueError: No JSON object could be decoded — causes and fixes

The Python 2 / requests error that means the response body was not JSON. How to see what actually came back, and the modern JSONDecodeError equivalent.

·5 min read

What causes "ValueError: No JSON object could be decoded"?

It means Python tried to parse something that was not JSON — most often an empty response body, an HTML error page, or plain text. The message is the Python 2 / older simplejson wording; Python 3 raises json.JSONDecodeError with a more specific message instead. The fix is always the same: print the raw text before parsing it.

Where the message comes from

You will see this exact wording from Python 2's json module, from simplejson, or from requests' response.json() when it wraps an older decoder. On Python 3 the equivalent failure reads Expecting value: line 1 column 1 (char 0).

# Python 2 / simplejson
ValueError: No JSON object could be decoded

# Python 3 — same root cause, clearer message
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Step 1 — look at what you actually received

Ninety percent of these are solved by one print statement. Never call .json() before you have seen the body at least once:

import requests

r = requests.get(url)
print(r.status_code)          # 500? 404? 302?
print(repr(r.text[:300]))     # '' or '<!DOCTYPE html>...' ?
print(r.headers.get("content-type"))

data = r.json()               # only after the above looks right

The four usual culprits

1. Empty body

A 204, a HEAD request, or a failed call can return zero bytes. json.loads("") always fails.

if not r.text.strip():
    raise RuntimeError(f"Empty body (HTTP {r.status_code})")

2. HTML instead of JSON

An error page, a login redirect, or a proxy notice. The body starts with < — see the dedicated guide on that error below.

ct = r.headers.get("content-type", "")
if "application/json" not in ct:
    raise RuntimeError(f"Expected JSON, got {ct}: {r.text[:200]}")

3. A Python dict repr, not JSON

Single quotes and True/None are Python syntax, not JSON. If the string came from str(some_dict), parse it with ast.literal_eval instead.

import ast, json

s = "{'name': 'Ada', 'active': True, 'note': None}"

json.loads(s)            # fails — not JSON
ast.literal_eval(s)      # {'name': 'Ada', 'active': True, 'note': None}

# Better: fix the producer to emit json.dumps(d)

4. Reading a file that was already consumed

Calling f.read() twice, or json.load(f) after the cursor reached EOF, yields an empty string.

with open("data.json") as f:
    text = f.read()
    # data = json.load(f)   # ✗ cursor is at EOF — empty!
    data = json.loads(text) # ✓

A safe wrapper

import json

def parse_json(text, source="input"):
    if text is None or not text.strip():
        raise ValueError(f"{source} was empty")
    try:
        return json.loads(text)
    except ValueError as e:          # catches JSONDecodeError too
        raise ValueError(
            f"{source} is not valid JSON: {e}\nFirst 200 chars: {text[:200]!r}"
        ) from e

Catching ValueError works on both Python 2 and 3, because JSONDecodeError subclasses it.

Paste your JSON into the validator →

Get the exact line, column, and a fix hint in seconds — no upload, no signup.

Open JSON Validator →

Frequently asked questions

Is 'No JSON object could be decoded' the same as JSONDecodeError?+

Effectively yes. It is the older Python 2 / simplejson wording for the same failure. Python 3 raises json.JSONDecodeError (a subclass of ValueError) with a more precise message such as 'Expecting value: line 1 column 1 (char 0)'.

Why does requests' response.json() raise it?+

Because the response body was not JSON — commonly an empty body, an HTML error page, or a redirect to a login form. Check response.status_code and print response.text before calling .json().

How do I parse a Python dict string with single quotes?+

Use ast.literal_eval(), not json.loads(). Single quotes, True, False, and None are Python literals rather than JSON. Better still, fix the producer to emit json.dumps().

Should I catch ValueError or JSONDecodeError?+

Catch ValueError if you need to support both Python 2 and 3, since JSONDecodeError subclasses it. On Python 3 only, catching json.JSONDecodeError is more precise and gives you .msg, .doc, and .pos.

Related tools & guides