Herramientas para desarrolladores
Errores de JSON: cómo encontrar y corregir un JSON no válido
Comas al final, comillas simples, comentarios, un BOM invisible: pasamos 26 fragmentos de JSON con errores por Chrome, Python y PHP y anotamos qué dice cada uno. Así se lee el mensaje y se corrige la causa real.
JSON es un formato estricto: basta una coma en el sitio equivocado para que se rechace el archivo entero. Y los mensajes de error no ayudan mucho. Chrome dice una cosa, Python otra y PHP a menudo solo dice «Syntax error». Para saber qué significa de verdad cada mensaje, escribimos 26 fragmentos cortos con los errores más habituales y los procesamos con Chrome 141, Node.js 22 (los dos usan el motor V8, igual que Microsoft Edge), Python 3.13 y PHP 8.3. Puedes pegar tu propio JSON en nuestro formateador de JSON: marca la línea y la columna del primer error y te dice en español la causa probable.
Cómo es un JSON válido
JSON está definido en dos documentos que coinciden entre sí: el RFC 8259 del IETF y ECMA-404. Las reglas caben en una pantalla:
- Un texto JSON es exactamente un valor: un objeto
{…}, un array[…], un texto (string), un número,true,falseonull. - Los textos y los nombres de las propiedades van entre comillas dobles rectas
". No se admiten comillas simples ni tipográficas. - Los elementos se separan con comas, pero después del último no va coma.
- No hay comentarios.
- Los números son decimales con punto: sin ceros a la izquierda (
007), sin coma decimal, sin hexadecimal (0xFF), sinNaNniInfinity. - Dentro de los textos hay que escapar las comillas dobles, las barras invertidas y los caracteres de control como saltos de línea y tabuladores:
\",\\,\n,\t. - Los espacios, tabuladores y saltos de línea entre elementos se admiten en cualquier sitio.
Muchas cosas que parecen JSON no lo son: un objeto literal de JavaScript, un diccionario de Python mostrado con print(), un archivo de configuración con comentarios. Se parecen lo bastante para engañar a la vista y se alejan lo bastante para fallar en cualquier parser.
El mismo error, tres mensajes distintos
Esto es lo que respondieron los tres parsers ante los errores más habituales. Chrome y Node.js dieron mensajes idénticos en todos los casos.
| Error | Chrome / Node.js | Python 3.13 | PHP 8.3 |
|---|---|---|---|
Coma antes de }: {"age": 31,} | Expected double-quoted property name in JSON at position 11 (line 1 column 12) | Illegal trailing comma before end of object: line 1 column 11 (char 10) | Syntax error |
Coma antes de ]: [1, 2, 3,] | Unexpected token ']', "[1, 2, 3,]" is not valid JSON | Illegal trailing comma before end of array | Syntax error |
Comillas simples: {'name': 'Anna'} | Expected property name or '}' in JSON at position 1 | Expecting property name enclosed in double quotes | Syntax error |
| Falta una coma entre dos propiedades | Expected ',' or '}' after property value in JSON | Expecting ',' delimiter | Syntax error |
Un // comentario en el archivo | Expected property name or '}' in JSON | Expecting property name enclosed in double quotes | Syntax error |
NaN como valor | Unexpected token 'N', … is not valid JSON | aceptado sin error | Syntax error |
| Salto de línea dentro de un texto | Bad control character in string literal in JSON | Invalid control character at | Control character error, possibly incorrectly encoded |
Ruta de Windows "C:\Users\anna" | Bad escaped character in JSON | Invalid \escape | Syntax error |
| Dos objetos seguidos | Unexpected non-whitespace character after JSON | Extra data | Syntax error |
| Entrada vacía | Unexpected end of JSON input | Expecting value: line 1 column 1 (char 0) | Syntax error |
| Una página HTML en lugar de JSON | Unexpected token '<', "<!DOCTYPE "... is not valid JSON | Expecting value: line 1 column 1 (char 0) | Syntax error |
Llaman la atención dos cosas. Python es el que más ayuda: nombra de forma explícita la coma sobrante y la marca de orden de bytes. PHP dice «Syntax error» casi siempre, así que el sitio lo tienes que buscar tú. Y Python aceptó NaN, que no es JSON válido. Volvemos a ello más abajo.
Cómo leer un mensaje de error
Cuando un mensaje incluye una posición, cuenta los caracteres desde cero: «position 11» es el carácter número 12, por eso Chrome añade «line 1 column 12». Python también cuenta líneas y columnas desde uno, pero señala la propia coma (columna 11). La posición marca el punto en el que el parser se rindió, que a menudo está un poco después del error real:
- Una coma antes de un cierre se indica en Chrome en la llave o el corchete, porque el parser solo nota el problema cuando espera otra propiedad y encuentra
}. - Una coma que falta se indica al principio del siguiente elemento, no al final del anterior.
- Un cierre que falta se indica al final del todo, por muy arriba que falte.
Un detalle en textos con emojis: JavaScript cuenta un emoji como dos caracteres, así que después de un emoji la columna de Chrome sale una de más. Python y nuestro formateador lo cuentan como uno.
Las versiones antiguas de los navegadores formulan los mismos errores de otra manera. El clásico es «Unexpected token < in JSON at position 0», y significa lo mismo que la fila de HTML de la tabla: el texto empieza por <.
Las causas más frecuentes y cómo resolverlas
Coma después del último elemento
JavaScript permite una coma tras el último elemento de un objeto o un array; JSON no. Suele aparecer al borrar o reordenar líneas a mano. Quita la coma que hay antes de } o ].
Comillas simples y tipográficas
JSON solo acepta la comilla doble recta ". Las comillas tipográficas (“ ” o « ») se cuelan cuando el JSON pasa por un procesador de textos, una app de mensajería o un programa de correo que «embellece» las comillas. Pega primero ese texto en un editor de texto plano o sustituye ahí las comillas.
Objetos de JavaScript y diccionarios de Python
Los nombres de propiedad sin comillas ({name: "Anna"}) son JavaScript válido, pero no JSON. En Python, print(datos) o str(datos) producen comillas simples y True, False y None, todo ello inválido. Usa JSON.stringify() en JavaScript y json.dumps() en Python, que siempre escriben JSON de verdad.
Comentarios
El JSON estándar no tiene comentarios. Algunas herramientas usan formatos ampliados que sí los permiten, como el «JSON with Comments» que Visual Studio Code utiliza para su configuración, o tsconfig.json en proyectos de TypeScript. Por eso un archivo funciona en un programa y falla en otro. Quita // … y /* … */ antes de enviar los datos a un parser estándar.
NaN, Infinity y undefined
JSON no tiene forma de escribir «no es un número» ni «infinito». JavaScript lo sabe: JSON.stringify() escribe null en su lugar. Python no: json.dumps(float('nan')) escribe NaN, que Python vuelve a leer sin quejarse pero que todos los navegadores rechazan. Con allow_nan=False en json.dumps() obtienes un error en lugar de JSON no válido. undefined no es un valor JSON; JSON.stringify() simplemente omite esas propiedades.
Ceros a la izquierda y coma decimal
Los códigos postales como 08001 (Barcelona) o los teléfonos que empiezan por 0 no son números en el sentido de JSON. Escríbelos como texto: "cp": "08001". Otra trampa típica en España es la coma decimal: {"precio": 0,5} se lee como el número 0 seguido de una coma, y después el parser espera un nuevo nombre de propiedad. Escribe 0.5. Si exportas desde Excel o una hoja de cálculo con configuración regional española, merece la pena revisarlo.
Saltos de línea y barras invertidas dentro de textos
No se admite un salto de línea o un tabulador real dentro de un texto; escribe \n y \t. La barra invertida inicia una secuencia de escape, así que en una ruta de Windows hay que duplicarla: "C:\\Users\\anna", o usar barras normales, que Windows también entiende en la mayoría de los casos.
HTML donde se esperaba JSON
Si el error apunta al primer carácter y menciona <, el servidor no envió JSON. Normalmente envió una página HTML: una página de error 404 o 500, una página de inicio de sesión o un aviso de mantenimiento. Comprueba el código de estado y la cabecera Content-Type de la respuesta antes de buscar un error de sintaxis.
Caracteres invisibles
Algunos editores y funciones de exportación guardan los archivos como «UTF-8 con BOM»: con una marca de orden de bytes invisible (U+FEFF) al principio. JSON.parse() la rechaza, y el mensaje de Python incluso sugiere la solución: decodificar el archivo con utf-8-sig. Según el RFC 8259, los programas no deben añadir un BOM al JSON que envían. Nuestro formateador lo elimina y te avisa.
Otros caracteres invisibles son igual de fatales fuera de los textos. En nuestras pruebas, Chrome y Python rechazaron un espacio de no separación (U+00A0), que suele colarse al copiar código de una página web o de un documento, y también los caracteres de anchura cero y las marcas de dirección que insertan los editores de árabe o persa. Dentro de los textos se admiten todos. Si el error señala un punto donde no ves nada raro, borra el espacio que hay ahí y vuelve a escribirlo.
Más de un valor
Dos objetos seguidos no son un texto JSON. Los archivos de registro y las exportaciones de datos usan a menudo JSON Lines (también llamado NDJSON), con un objeto por línea. Esos archivos están bien, pero cada línea hay que procesarla por separado.
Válido, pero sorprendente
Hay JSON válido que aun así no hace lo que esperas:
| Entrada | Chrome / Node.js | Python 3.13 | PHP 8.3 |
|---|---|---|---|
{"a": 1, "a": 2} | a vale 2 | a vale 2 | a vale 2 |
{"id": 12345678901234567890} | 12345678901234567000 | 12345678901234567890 (exacto) | 1.2345678901234567E+19 (coma flotante) |
La segunda fila importa en la práctica. JavaScript y PHP guardan los números como valores de coma flotante de 64 bits, que solo son exactos hasta 9.007.199.254.740.991 (2^53 − 1). El RFC 8259 recomienda no salir de ese rango. Por eso los identificadores grandes, por ejemplo de bases de datos o redes sociales, conviene enviarlos como texto.
En la otra dirección, JSON.stringify() omite sin avisar las propiedades cuyo valor es undefined o una función, convierte NaN e Infinity en null y las fechas en texto. Un BigInt incluso provoca un error («Do not know how to serialize a BigInt»).
Lista de comprobación cuando se rechaza un JSON
- Comprueba que de verdad has recibido JSON: código de estado 200 y
Content-Typeapplication/json. - Pega el texto en un formateador y mira la línea indicada y el carácter anterior.
- Busca a los sospechosos habituales: una coma al final, comillas simples o tipográficas, un comentario, un nombre sin comillas.
- Revisa los valores:
True/Nonede Python,NaN, números con ceros a la izquierda o coma decimal. - Revisa los textos: saltos de línea reales, barras invertidas simples en rutas, espacios invisibles.
- Corrige el programa que genera el JSON para que el error no vuelva: usa
JSON.stringify(),json.dumps()ojson_encode()en lugar de montar el JSON a mano.
Herramientas de esta guía
Preguntas frecuentes
¿Por qué JSON no admite una coma después del último elemento?
La gramática del RFC 8259 y de ECMA-404 simplemente no la contempla, y el formato se mantiene sin cambios a propósito para que todos los parsers lean los mismos datos de la misma manera. Formatos ampliados como JSON5 admiten comas finales y comentarios, pero un parser JSON estándar los rechaza.
¿Puedo poner comentarios en un JSON?
En JSON estándar, no. Algunos programas aceptan comentarios en sus propios archivos de configuración, como los ajustes de Visual Studio Code o el tsconfig.json de TypeScript, pero los datos que envías a una API o lees con JSON.parse() no deben contener ninguno. Si necesitas notas, añádelas como una propiedad normal, por ejemplo "_comentario".
¿Qué significa «Unexpected token < in JSON at position 0»?
El texto empieza por «<», así que casi seguro es HTML, por ejemplo una página de error o de inicio de sesión, y no JSON. Las versiones nuevas de Chrome lo formulan como Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Comprueba la dirección a la que llamas y el código de estado de la respuesta.
¿Por qué Python acepta NaN si los navegadores lo rechazan?
El módulo json de Python lee y escribe por defecto NaN, Infinity y -Infinity con sus nombres de JavaScript, aunque no forman parte del estándar JSON. Con json.dumps(datos, allow_nan=False) te aseguras de generar solo JSON válido.
¿Se sube a algún sitio el JSON que pego en el formateador?
No. Nuestro formateador de JSON funciona por completo en tu navegador. El texto se comprueba y se formatea en tu dispositivo y no se envía a ningún servidor.
¿Por qué mi identificador grande cambia al leerlo?
JavaScript guarda todos los números como valores de coma flotante de 64 bits, que solo son exactos hasta 9.007.199.254.740.991. Los enteros mayores se redondean. Envía esos identificadores como texto, por ejemplo "id": "12345678901234567890".
Fuentes y lecturas recomendadas
- RFC 8259 – The JavaScript Object Notation (JSON) Data Interchange Format (www.rfc-editor.org)
- ECMA-404 – The JSON data interchange syntax (2nd edition, 2017) (ecma-international.org)
- MDN Web Docs: JSON.parse() (developer.mozilla.org)
- Python documentation: json – JSON encoder and decoder (docs.python.org)