Skip to content
JSON Tidy
JSON errors

Python JSONDecodeError: Expecting value

Python raises this error when it reaches a point where a JSON value should start, such as a string, number, object, or array, and finds something else.

The position at the end of the message tells you which kind of problem you have. line 1 column 1 (char 0) means the very first character is wrong, so the text is empty or isn’t JSON at all. A later position points to one bad value inside otherwise valid JSON.

Quick fix

  1. Print response.status_code, the Content-Type header, and response.text[:200] before parsing.
  2. If the text is empty or starts with <, fix the request: the URL, the authentication, or the server error.
  3. If the error is at a later column, look at the value at that position. It’s often a single-quoted string or a missing value.

Find and fix it in the validator

Paste your broken JSON into the JSON validator. It underlines the first error as you type, and Fix My JSON repairs common mistakes such as missing quotes and extra commas. It runs in your browser, so your JSON isn’t uploaded.

This example has a missing value and a single-quoted string. Fix My JSON fills in the missing value with null and switches the single quotes to double quotes.

{
  "id": 7,
  "owner": ,
  "status": 'active'
}

The message in each browser and language

You might see any of these, depending on where the JSON is parsed:

WhereMessage
json.loads, json.loadjson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
requests (response.json())requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
JavaScript, same inputUnexpected end of JSON input, or Unexpected token '<'

Line 1 column 1: the text is empty or not JSON

  • An empty response. The API returned 204 No Content, or an error status with no body. response.text is an empty string.
  • An HTML page. A 403 from a firewall, a 404, a login redirect, or a 502 from a proxy. response.text starts with <. The unexpected token guide lists the usual reasons a server sends HTML.
  • A file that’s already been read. After f.read(), the file position is at the end, so a following json.load(f) sees nothing. Parse the string you read with json.loads, or call f.seek(0) first.
  • A file opened with mode "w". Opening a file for writing empties it immediately, before anything reads it. Open it with "r" to load it.
  • Plain text or JSONP. Responses like OK or callback({...}) aren’t JSON.

Printing the status code, content type, and the start of the body before parsing usually shows the cause. The code below calls raise_for_status() and then checks the content type, so a bad response raises a clear error instead of JSONDecodeError:

import requests

response = requests.get("https://api.example.com/users", timeout=10)
response.raise_for_status()

content_type = response.headers.get("Content-Type", "")
if "application/json" not in content_type:
    raise ValueError(f"Expected JSON, got {content_type}: {response.text[:200]!r}")

users = response.json()

A later column: one value is invalid

When the column is past the start, the text is mostly valid JSON with one bad value. JSONDecodeError records where it stopped, so you can print the text around that point:

try:
    data = json.loads(text)
except json.JSONDecodeError as error:
    print(f"Line {error.lineno}, column {error.colno}")
    print(text[max(0, error.pos - 30):error.pos + 30])

The value at or just before that point is usually one of these:

  • A single-quoted string, such as "status": 'active'. JSON strings need double quotes.
  • A missing value, such as "owner": followed directly by a comma.
  • A trailing comma in an array. [1, 2,] fails at the ]. See fixing trailing commas.
  • JavaScript’s undefined, or Python’s None and True. Python values usually mean the text was written with str() or print() instead of json.dumps().

A property name in single quotes gives a different error: Expecting property name enclosed in double quotes.

Files saved with a byte order mark

Some Windows editors save JSON as “UTF-8 with BOM”, even though the JSON standard says not to add one. json.loads then raises Unexpected UTF-8 BOM (decode using utf-8-sig), and other readers may fail on the first character. Open the file with the utf-8-sig encoding, which reads files with or without the mark:

import json

with open("settings.json", encoding="utf-8-sig") as f:
    settings = json.load(f)

Still stuck? Paste your JSON into the validator to see exactly where the first error is, or try Fix My JSON.

Sources and further reading