Expecting value: line 1 column 1 (char 0)
Python te está diciendo que el primer carácter de lo que le has pasado no es el comienzo de un valor JSON. En la práctica esto casi nunca significa que tu JSON esté sutilmente mal formado. Significa que lo que has parseado no es JSON: una página HTML de error, un cuerpo vacío, un mensaje de texto plano, o nada en absoluto. La posición es línea 1 columna 1 porque el parser falló antes de leer nada.
También puede que lo veas escrito así
- json.decoder.JSONDecodeError: Expecting value
V8 reescribió la mayoría de sus mensajes de error de JSON en 2022. La redacción antigua sigue siendo la que muestran casi todos los resultados de búsqueda, pero ningún runtime actual la emite.
Pega tu JSON y ve exactamente dónde se rompe
Nada de lo que pegues sale de tu navegador. La lista de permitidos de connect-src convierte eso en una garantía del navegador, no en una promesa. Compruébalo tú mismo
Qué lo provoca realmente
Ordenados por la frecuencia con la que cada uno acaba siendo la respuesta.
-
01 El cuerpo de la respuesta es HTML, no JSON
Una página 404, una redirección al login, un desafío de Cloudflare o una página de error de un proxy. La petición tuvo éxito a nivel HTTP y devolvió un documento, solo que no el que esperabas. Imprime los primeros cientos de caracteres antes de parsear y lo verás al instante.
Falla
r = requests.get(url) data = r.json() # JSONDecodeErrorFunciona
r = requests.get(url) if r.status_code != 200 or 'json' not in r.headers.get('content-type', ''): raise RuntimeError(f"{r.status_code} {r.headers.get('content-type')}: {r.text[:300]}") data = r.json() -
02 El cuerpo está vacío
Un HTTP 204, una petición limitada por rate limit que no devolvió nada, un WAF que descartó el cuerpo, o un archivo que existe pero tiene cero bytes. Una cadena vacía no es JSON válido. Los documentos válidos más cortos son null, 0, "", [] y {}.
Falla
json.loads("")Funciona
text = r.text if not text.strip(): return None data = json.loads(text) -
03 Estás leyendo un archivo que no es JSON
Un CSV, un archivo de log, un pickle de Python, o un archivo JSON Lines donde cada línea es un documento aparte. Para JSON Lines, parsea línea a línea en vez de todo de golpe.
Falla
with open('events.jsonl') as f: data = json.load(f) # Expecting value, or Extra dataFunciona
with open('events.jsonl') as f: data = [json.loads(line) for line in f if line.strip()] -
04 La autenticación falló y la API devolvió un mensaje de texto plano
Muchas APIs devuelven "Unauthorized" o "Forbidden" como texto plano con un 401 o un 403, y no como JSON. Comprueba el código de estado antes de mirar el cuerpo.
El mismo error en otros entornos
El problema de fondo es idéntico; solo cambia la redacción. Si un compañero reporta alguno de estos, está mirando exactamente lo mismo que tú.
| JavaScript (V8) | Unexpected token '<', "<!DOCTYPE "... is not valid JSON |
|---|---|
| JavaScript (V8), empty input | Unexpected end of JSON input |
| C# (Newtonsoft) | Unexpected character encountered while parsing value: <. Path '', line 0, position 0. |
Preguntas frecuentes
- ¿Por qué siempre dice línea 1 columna 1?
- Porque el parser falló en el primerísimo carácter. Nunca llegó lo bastante lejos como para alcanzar la línea 2. Si tu error menciona una línea y columna posteriores, el problema está genuinamente dentro de tu JSON y es otro error distinto.
- ¿Cómo veo lo que he recibido realmente?
- Imprime response.text[:500] antes de llamar a .json(). En nueve de cada diez casos la respuesta se ve en la primera línea: un <!DOCTYPE html>, una cadena vacía, o un mensaje corto en texto plano.
- ¿json.loads acepta un objeto bytes?
- Sí, desde Python 3.6 json.loads acepta bytes y bytearray además de str, y detecta UTF-8, UTF-16 y UTF-32. Así que este error no lo causa pasar bytes.
Arréglalo ahora
Pega el payload en la herramienta de arriba, o ve directamente a la creada para este trabajo.
Valida tu JSON