Expecting value: line 1 column 1 (char 0)

Python got to a spot where a value has to begin and found something that cannot begin one. Read the offset first. char 0 means the document was empty, or was never JSON at all. Any larger number means one bad value inside a document that is otherwise fine.

How the message appears

CPython's json module reports line, column and character offset. The offset counts characters from the start of the document, which is the one to trust when the whole document is a single line.

  • json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  • json.decoder.JSONDecodeError: Expecting value: line 3 column 13 (char 32)
  • requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The fix

Swap the Python literals for JSON ones. None becomes null, True becomes true, False becomes false. And if the offset says char 0, stop reading the JSON. Go and look at what the server actually sent back.

Fails to parse
{
  "name": "Ada",
  "active": None
}
Parses
{
  "name": "Ada",
  "active": null
}

The example travels in the URL fragment, which browsers never send to a server.

What causes it

The body was empty (this is what char 0 usually means)

A 204 No Content, a HEAD request, a redirect that returned nothing, or a connection that dropped. response.json() ends up calling json.loads(""), and there is no value in an empty string. Print repr(response.text[:200]) and you will usually see the problem straight away.

It is an HTML page, not JSON

Position 0 is <. A 404 page, a login redirect, a proxy error, a bot check. Plenty of setups return HTML with a cheerful 200 on every one of them. Call response.raise_for_status() before you parse and most turn into an error that names the real problem.

Python literals in the document

JSON spells them null, true and false. Python spells them None, True and False. Any document built with str(some_dict), or copied out of a repr, carries the Python spellings. That is what the example below shows.

You passed a path instead of the contents

json.loads("data.json") tries to parse the filename itself, all nine characters of it. Use json.load(f) with an open file, or json.loads(path.read_text()).

Worth knowing

  • Never build JSON with str(obj) or an f-string. json.dumps(obj) is the one thing that turns a Python object into valid JSON, and it is also what escapes the quotes for you.
  • Python is looser than JSON in one place that catches people out. json.loads accepts NaN, Infinity and -Infinity by default, and json.dumps will happily write them. Pass allow_nan=False when the document has to be read by anything else.
  • The exception carries the numbers. except json.JSONDecodeError as e: gives you e.lineno, e.colno, e.pos and e.doc, all far more useful in a log than the printed string.

Written by Vishnu Shankar.

Your JSON and text are processed entirely in your browser and are never sent anywhere. To understand how the site is used we record each page view (the page, the referrer and an approximate location from your IP address) and keep it for 90 days. Nothing is stored on your device unless you choose it. Accept to also store a device identifier here and record your browser and device characteristics, which is what lets a return visit be told from a new one. Decline and nothing is sent at all. We respect Do Not Track. See our privacy policy.