Skip to content

Python

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Python looked for a JSON value at the first character and found nothing it could use: the text is empty or starts with something that is not JSON.

Updated · 3 min read

console
Python jsonjson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
requests 2.27+requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Flask get_json(), debug modeFailed to decode JSON object: Expecting value: line 1 column 1 (char 0)
Django REST frameworkJSON parse error - Expecting value: line 1 column 1 (char 0)

Find it in your JSON

Paste the text that failed. The checker points at the exact spot, and Repair fixes most syntax mistakes.

Input · JSON

Invalid JSON

Line 1, column 1

Unexpected '<': this looks like HTML or XML, not JSON.

1<!doctype html> ^2<html lang=en>

Fix: A server often sends an HTML error or login page where JSON was expected. Check the URL, the status code and the Content-Type header.

Why this happens
Invalid JSONRuns in your browser

Load from a URL

Your browser fetches it directly, so the server must allow cross-origin requests (CORS).

Why it happens

Python raises JSONDecodeError: Expecting value: line 1 column 1 (char 0) when the first character of the text cannot start a JSON value. Three kinds of input cause it, most common first.

1. An HTML page. The server sent an error, login or block page, like the Flask 500 page in the checker above. Some sites block the default python-requests User-Agent and answer with an HTML 403 page.

2. Nothing at all. json.loads("") fails at char 0. Empty text comes from a 204 No Content response, a HEAD request, an empty file, or a file object that was already read:

import json

with open("settings.json") as f:
    print(f.read())  # consumes the file
    json.load(f)     # reads "" -> Expecting value: line 1 column 1 (char 0)

3. Text that is not JSON. A plain-text body such as Internal Server Error, a streamed line that still starts with data: , a file name passed to json.loads("settings.json") instead of the file’s content, or a UTF-8 byte order mark decoded as .

A column above 1 means the start was fine: json.loads("[None]") fails at line 1 column 2 (char 1), because JSON writes it null.

How to fix JSONDecodeError: Expecting value

Look at what arrived before parsing it. If you copy a response by hand, the JSON validator names HTML, empty input and invisible characters instead of only giving a position.

requests

import requests

resp = requests.get("https://api.example.com/v2/invoices/7781", timeout=10)
resp.raise_for_status()  # 4xx and 5xx error pages stop here

if not resp.content:  # 204 No Content or an empty 200
    invoice = None
elif "json" not in resp.headers.get("Content-Type", ""):
    raise ValueError(f"Expected JSON, got {resp.headers.get('Content-Type')}: {resp.text[:200]!r}")
else:
    invoice = resp.json()

httpx

r.json() is plain json.loads(r.content), so httpx raises the standard json.JSONDecodeError. Log the response when it fails:

import json
import httpx

r = httpx.get("https://api.example.com/v2/invoices/7781")
try:
    invoice = r.json()
except json.JSONDecodeError:
    print(r.status_code, r.headers.get("content-type"), r.text[:200])
    raise

Files

import json
from pathlib import Path

text = Path("settings.json").read_text(encoding="utf-8-sig")  # drops a BOM if present
settings = json.loads(text) if text.strip() else {}

utf-8-sig reads files with or without a byte order mark. With plain utf-8, a BOM raises Unexpected UTF-8 BOM (decode using utf-8-sig) instead. On Windows, Python 3.14 and older open files in the ANSI code page by default, which turns the BOM into  and gives this error.

Flask

request.get_json() answers a body that does not parse with 400 Bad Request (debug mode adds Failed to decode JSON object: ...) and a non-JSON Content-Type with 415. A form post leaves request.data empty, because the fields go to request.form, so json.loads(request.data) fails at char 0. Reply with a clear 400 instead:

from flask import Flask, abort, request

app = Flask(__name__)

@app.post("/invoices")
def create_invoice():
    payload = request.get_json(silent=True)
    if payload is None:
        abort(400, description="Send a JSON body with Content-Type: application/json")
    return {"received": payload}, 201

Django

json.loads(request.body) fails the same way when the client sends form data or nothing. Check the content type first:

import json
from django.http import HttpResponseBadRequest, JsonResponse

def create_invoice(request):
    if request.content_type != "application/json":
        return HttpResponseBadRequest("Expected Content-Type: application/json")
    try:
        payload = json.loads(request.body)
    except json.JSONDecodeError as exc:
        return HttpResponseBadRequest(f"Invalid JSON: {exc}")
    return JsonResponse({"received": payload}, status=201)

Django REST framework’s JSON parse error - Expecting value response means the client sent Content-Type: application/json with a body that is not JSON: fix the client.

Same error in other languages

Other parser messages, grouped by language: common JSON errors.

Questions

What do line, column and char mean?

Line and column count from 1. Char is the zero-based offset into the whole text. Whitespace-only text fails after the whitespace: three spaces give line 1 column 4 (char 3), a lone newline gives line 2 column 1 (char 1).

Why does the traceback show two JSONDecodeErrors?

requests catches the json error and raises its own with the same message, so Python prints both, joined by "During handling of the above exception, another exception occurred". One fix clears both.

Which exception should I catch?

json.JSONDecodeError, a subclass of ValueError. Since requests 2.27, requests.exceptions.JSONDecodeError inherits from it, so the same except clause catches both.

Related errors

All common JSON errors

Esc