Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

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

Verified FixPython 3.10+requests 2.x / Python NativeSilo: requests

Quick Fix / Solution Rapide

Verify that response.status_code == 200, check response.text.strip() is not empty, and ensure Content-Type contains application/json before calling response.json().

Root Cause Analysis

This error occurs when Python tries to parse a string into a Python dictionary via response.json() or json.loads(), but the response body is either completely empty (0 bytes) or begins with non-JSON content (such as an HTML < character from a 404 or 502 error page).

How the JSON Parser Evaluates Input

The standard library json.loads() scanner expects the first non-whitespace character in a valid JSON document to be {, [, ", a number, true, false, or null. When char 0 (the first character) does not match any valid JSON token:

  1. Empty Body (204 No Content or 201 Created without body): The scanner encounters immediate end-of-string, raising JSONDecodeError: Expecting value: line 1 column 1 (char 0).
  2. HTML Error Pages (404 Not Found / 500 Server Error): The web server returns <!DOCTYPE html><html>.... The < character on line 1 column 1 is not a valid JSON literal.
  3. Reverse Proxy Timeouts (502 Bad Gateway / 504 Gateway Timeout): Cloudflare or Nginx returning raw HTML gateway timeout pages instead of upstream JSON.

Reproduction Code (MCVE)

Example: Bug Reproduction
import json

# Simulates calling response.json() on an empty HTTP response body
empty_response_text = ''
json.loads(empty_response_text)

Solution 1: Validate Status Code and Content-Type Before Parsing

Inspect the HTTP response headers and status code to guarantee a non-empty JSON payload before calling response.json().

Example: Recommended Solution
import requests

def parse_api_response(response: requests.Response) -> dict:
    # 1. Raise HTTP errors early for 4xx/5xx responses
    response.raise_for_status()
    
    # 2. Check for empty response payloads (e.g. 204 No Content)
    if not response.text or not response.text.strip():
        return {}
        
    # 3. Parse JSON safely
    return response.json()

# Simulate with mock response
mock_resp = requests.Response()
mock_resp.status_code = 200
mock_resp._content = b'{"status": "success", "code": 200}'
data = parse_api_response(mock_resp)
print(f'Successfully parsed response payload: {data}')

Solution 2: Defensive JSON Extraction Helper with Fallback

Wrap response.json() in a try...except json.JSONDecodeError block to return fallback data when APIs return unexpected text formats.

Example: Alternative Solution
import json

def safe_json_extract(raw_text: str, default: dict = None) -> dict:
    if default is None:
        default = {}
    try:
        return json.loads(raw_text)
    except json.JSONDecodeError as exc:
        print(f'Warning: Non-JSON payload received ({exc}). Returning fallback.')
        return default

result = safe_json_extract('<html>404 Not Found</html>', {'error': 'Non-JSON response'})
print(f'Fallback result: {result}')

Common Pitfalls & Edge Cases

A dangerous practice is assuming response.ok (status < 400) guarantees valid JSON. An endpoint returning 200 OK with a raw CSV, XML, or plain text string ('OK') will still fail on response.json(). Always inspect response.headers.get('Content-Type').

Contrasting with similar errors:

  • JSONDecodeError: Expecting value: line 1 column 1 (char 0): Completely empty text or HTML < at start.
  • JSONDecodeError: Extra data: line 1 column 10 (char 9): Multiple concatenated JSON objects (e.g. {}{}).
  • TypeError: Object of type X is not JSON serializable: Occurs when encoding Python objects to JSON, not when decoding strings.