Errores comunes en JSON: JSON inválido, pérdida silenciosa de datos y cómo corregir cada uno

JSON es estricto y pequeño, y por eso los mismos pocos errores causan casi todos los fallos. Algunos rompen el parser al instante y el mensaje señala más o menos dónde está el problema. Los más caros se analizan sin error y cambian los datos en silencio: un ID grande redondeado, una clave duplicada sobrescrita, una fecha convertida en texto o un campo que desaparece. De los primeros verás el mensaje exacto; de los segundos, el síntoma que los delata.

Errores de sintaxis que detecta el parser

Aparecen al escribir JSON a mano o al tratar un objeto literal de JavaScript como si fuera JSON. Los mensajes son los de V8, el motor de Chrome y Node.js.

{"a": 1,}          coma final
                   -> Expected double-quoted property name in JSON at position 8 (line 1 column 9)
{a: 1}             clave sin comillas
                   -> Expected property name or '}' in JSON at position 1
{'a': 'x'}         comillas simples: solo valen las dobles
{"a": 01}          cero inicial en un número
{"a": .5}          el número debe empezar por un dígito: 0.5
{"a": NaN}         NaN, Infinity y undefined no existen en JSON
{"a": "línea1
línea2"}           salto de línea literal dentro de un string: debe ser \n
// comentario      los comentarios no forman parte de JSON

La posición del error es un desplazamiento en caracteres dentro del texto. El fallo está en esa posición o justo antes. Una coma que falta entre dos propiedades suele señalarse al inicio de la segunda propiedad, no donde debería ir la coma.

Si el archivo debe admitir comentarios y comas finales, como tsconfig.json o la configuración de VS Code, no es JSON sino JSONC o JSON5. Léelo con un parser de ese formato en lugar de pelear con JSON.parse.

Strings y escapes

Dentro de un string hay que escapar " y \, y también todos los caracteres de control por debajo de U+0020, incluidos el tabulador y el salto de línea. Los escapes válidos son \", \\, \/, \b, \f, \n, \r, \t y \uXXXX. Cualquier otro, como \x41, \' o \a, es un error.

Las rutas de Windows son una fuente habitual de problemas: "C:\new\table" es JSON válido, pero significa C:, un salto de línea, ew, un tabulador y able. La ruta queda mal sin ningún aviso. Escribe "C:\\new\\table" o usa barras normales, que las API de Windows aceptan.

Nunca construyas JSON concatenando strings. '{"nombre": "' + nombre + '"}' falla en cuanto un nombre contiene una comilla, y en contextos HTML se convierte en un fallo de inyección. Usa el serializador del lenguaje.

Números que cambian sin ningún error

JSON no limita el tamaño ni la precisión de los números, pero la mayoría de los parsers los convierten a flotantes de 64 bits. En JavaScript, los enteros por encima de Number.MAX_SAFE_INTEGER (9007199254740991) se redondean:

JSON.parse('{"id": 12345678901234567890}').id
// 12345678901234567000   <- otro ID distinto, sin ningún error

Por eso las API con IDs de 64 bits, como los snowflake IDs o las claves BIGINT, los envían también como string (por ejemplo id_str en Twitter/X). Envía los identificadores grandes y los importes como strings, o usa un parser que soporte números grandes.

Los decimales tienen el mismo problema: 0.1 + 0.2 no es 0.3 en coma flotante. Para dinero, envía unidades mínimas enteras ("importeCentimos": 1999) o strings decimales ("importe": "19.99").

Claves duplicadas

RFC 8259 dice que los nombres de un objeto DEBERÍAN ser únicos, pero no prohíbe los duplicados, y los parsers no se ponen de acuerdo. JavaScript, Python y PHP se quedan con el último valor; otros se quedan con el primero o rechazan el documento.

{"rol": "usuario", "rol": "admin"}
JSON.parse(...)  ->  { rol: "admin" }

Es un problema de seguridad cuando dos componentes leen el mismo documento, por ejemplo un validador que ve el primer rol y una aplicación que usa el último. Los duplicados suelen aparecer al fusionar objetos a mano o al generar JSON con plantillas.

Lo que JSON.stringify elimina o cambia sin avisar

JSON.stringify({ a: undefined, b: () => 1, c: NaN, d: new Date(0), e: [undefined] })
// '{"c":null,"d":"1970-01-01T00:00:00.000Z","e":[null]}'
//   a y b desaparecen; NaN pasa a null; la fecha pasa a string; undefined en un array pasa a null

const o = {}; o.self = o;
JSON.stringify(o);   // TypeError: Converting circular structure to JSON

Las propiedades undefined, las funciones y los símbolos desaparecen, así que un campo opcional falta en lugar de ser null, y quien recibe los datos no distingue "sin valor" de "no enviado". NaN e Infinity pasan a null. Los objetos Date se convierten en strings ISO y vuelven de JSON.parse como strings, no como fechas, así que hay que reconvertirlos a propósito. Map y Set se convierten en {}, y BigInt lanza un TypeError salvo que definas toJSON.

Problemas a nivel de archivo

Una marca de orden de bytes UTF-8 (EF BB BF) al inicio del archivo, que añaden algunos editores de Windows, hace que los parsers estrictos fallen en la posición 0 con un error de "unexpected token". Guarda como UTF-8 sin BOM o elimínala antes de analizar.

JSON debe ser UTF-8 cuando se intercambia entre sistemas (RFC 8259). Un archivo guardado en UTF-16 o Latin-1 falla o se analiza con textos corruptos.

Varios documentos JSON en un mismo archivo ({…}{…}) no son JSON válido. Son JSON Lines si cada documento va en su propia línea, y necesitan un lector línea a línea.

Cómo validar y corregir paso a paso

Pega el texto en un formateador o validador de JSON para obtener línea y columna en lugar de una posición en caracteres. Corrige solo el primer error indicado y vuelve a validar, porque una comilla que falta puede provocar una cascada de errores engañosos más abajo. Si el JSON es válido pero los valores están mal, compara el texto original con el resultado analizado y revisa primero los números grandes, las claves duplicadas y los campos que faltan.

jq . datos.json           # valida y formatea; indica línea y columna si hay error
python3 -m json.tool datos.json

Fuentes y lecturas recomendadas

RFC 8259 es la especificación de JSON: escapes de strings, gramática de números, nombres duplicados y el requisito de UTF-8. ECMA-404 es el estándar equivalente de Ecma. MDN documenta JSON.parse, JSON.stringify y Number.MAX_SAFE_INTEGER, y la documentación de JSON5 y de JSONC (VS Code) describe los formatos más flexibles.

Frequently asked questions

¿Por qué mi JSON es inválido?

Las causas más habituales son una coma final, comillas simples, claves sin comillas, comentarios, `NaN` o `undefined`, y saltos de línea o barras invertidas sin escapar dentro de strings. Un validador muestra la línea y la columna del primer error.

¿JSON admite comas finales?

No. Una coma después del último elemento de un objeto o array es un error de sintaxis en JSON. JSON5 y JSONC sí la admiten, pero `JSON.parse` no.

¿Se pueden poner comentarios en JSON?

No. Los comentarios no forman parte de JSON. Los archivos que los necesitan suelen ser JSONC o JSON5 y deben leerse con un parser de ese formato.

¿Por qué cambió mi número grande después de JSON.parse?

JavaScript guarda los números como flotantes de 64 bits, exactos solo hasta 9007199254740991. Los enteros mayores se redondean sin error. Envía los IDs grandes como strings.

¿Qué pasa con las claves duplicadas en JSON?

La especificación las desaconseja pero no las prohíbe. JavaScript, Python y PHP se quedan con el último valor, y otros parsers pueden quedarse con el primero o rechazar el documento.

¿Cómo valido un JSON online?

Pégalo en un validador o formateador de JSON que funcione en el navegador: te indica la línea y la columna del primer error y formatea el resultado si es válido. Evita subir JSON con datos privados a servicios que envían el texto a un servidor.