Unexpected end of JSON input : ce que signifie l'erreur et comment la corriger
L'analyseur est arrivé au bout du texte avant de trouver une valeur complète et, dans la plupart des cas, c'est parce qu'il n'y avait pas de texte du tout.
Vous appelez JSON.parse() ou response.json() et obtenez SyntaxError: Unexpected end of JSON input. Contrairement à un token inattendu, cette erreur ne désigne pas un caractère fautif. Elle signifie que l'analyseur a atteint le dernier caractère alors qu'il attendait encore une valeur. Soit le texte était vide, soit il s'arrête au milieu d'un document. Tout le travail consiste à distinguer ces deux cas, et une seule ligne de log y suffit.
La même erreur selon l'environnement
| Où elle apparaît | Message |
|---|---|
Chrome, Edge et Node.js, JSON.parse | Unexpected end of JSON input |
Chrome et Edge, response.json() | Le même, parfois précédé de Failed to execute 'json' on 'Response': |
Chrome, une réponse sans aucun corps, comme une réponse opaque no-cors | Unexpected end of input |
| Firefox | JSON.parse: unexpected end of data at line 1 column 1 of the JSON data |
| Safari | JSON Parse error: Unexpected EOF |
Python, json.loads | Expecting value: line 1 column 1 (char 0) |
Quand un message mentionne la ligne 1, la colonne 1 ou le caractère 0, l'analyseur n'a rien trouvé à lire. Un texte vide n'est pas du JSON valide : les documents valides les plus courts sont des valeurs comme 0, null, "" ou {}.
La cause habituelle : un corps vide ou une chaîne vide
Avant de chercher un bug dans les données, vérifiez qu'il y avait bien des données. Voici les sources courantes d'une chaîne vide :
- 204 No Content. Beaucoup d'API répondent à un
DELETEou à unPUTréussi par un statut 204 sans corps.res.okvauttruepour un 204 : le vérifier ne protège donc pas l'appelres.json()qui suit. - Les requêtes HEAD. Une réponse à
HEADn'a jamais de corps, quoi qu'en disent ses en-têtes. - fetch avec
mode: 'no-cors'. Ce mode ne contourne pas CORS. Il renvoie une réponse opaque, de statut 0, dont votre code n'a pas le droit de lire le corps : l'analyse échoue donc systématiquement. Activez CORS sur le serveur ou appelez l'API via votre propre backend. - Un 200 ou un 500 sans contenu. Un gestionnaire qui n'écrit jamais sa sortie, ou une erreur serveur qui termine la requête avant tout envoi, produit un corps vide avec un code de statut ordinaire.
- Des valeurs stockées ou des fichiers vides.
localStorage.setItem('cart', ''), unsettings.jsonvide ou une variable d'environnement définie mais vide transmettent tous une chaîne vide àJSON.parse. - Un saut de ligne final dans du JSON délimité par lignes.
text.split('\n').map(JSON.parse)échoue sur la chaîne vide qui suit le dernier saut de ligne. Filtrez d'abord les lignes vides.
Deux cas voisins se comportent différemment. Une clé localStorage absente renvoie null, et JSON.parse(null) renvoie null sans lever d'erreur. Une variable non définie vaut undefined, qui échoue avec un autre message, "undefined" is not valid JSON ; ce cas est traité dans Unexpected token in JSON.
Pour savoir dans quel cas vous êtes, lisez le corps comme du texte avant de l'analyser :
const res = await fetch('/api/cart', { method: 'DELETE' });
const text = await res.text();
console.log(res.status, res.type, res.headers.get('content-type'), text.length);
// Résultats typiques :
// 204 basic null 0 -> rien à analyser
// 0 opaque null 0 -> une requête no-cors
// 200 basic application/json 8192 -> il y a du texte : vérifier qu'il est complet
Dans le navigateur, le panneau Réseau (Network) montre la même chose : un onglet Réponse vide signifie que le problème vient de la requête ou du serveur, pas de votre code d'analyse.
Quand le texte a été tronqué
Si le texte n'est pas vide, il s'est arrêté avant la fin du document. Les versions actuelles de Chrome et de Node.js n'affichent « Unexpected end of JSON input » que lorsque le texte s'arrête là où une valeur devrait commencer, ou au milieu de true, false ou null. Partout ailleurs, la même troncature produit un autre message :
| Le texte se termine par | Message dans les versions actuelles de Chrome et Node.js |
|---|---|
{"id": | Unexpected end of JSON input |
{"tags":["a", | Unexpected end of JSON input |
{"ok":tru | Unexpected end of JSON input |
{"name":"Al (11 caractères) | Unterminated string in JSON at position 11 |
{"id":42 (8 caractères) | Expected ',' or '}' after property value in JSON at position 8 |
La règle à retenir : si la position indiquée dans le message est égale à la longueur du texte, le document a été tronqué, et non mal formé. Les anciennes versions de Chrome et de Node.js signalaient aussi la plupart de ces cas par « Unexpected end of JSON input » : les anciens journaux et les réponses plus anciennes trouvées en ligne emploient donc cette formulation pour toute troncature.
Un JSON tronqué provient en général de l'une de ces sources :
- L'analyse d'un flux morceau par morceau. Les données issues de
res.body.getReader(), d'un événementdatade Node.js ou d'un socket arrivent en fragments de taille arbitraire. Un fragment isolé est rarement un document complet. Rassemblez les fragments et analysez une seule fois à la fin, ou mettez en tampon le JSON délimité par lignes jusqu'à chaque saut de ligne. - Un stockage trop petit. Une colonne de base de données plus courte que le document, dans une base configurée pour tronquer au lieu de rejeter l'écriture, conserve le début et perd la suite.
- Une copie depuis un outil qui raccourcit les textes longs, comme un système de journalisation qui limite la longueur des lignes.
- Une connexion fermée prématurément sur une réponse sans
Content-Lengthet non découpée en chunks. Quand la longueur est déclarée, une connexion interrompue est signalée comme une erreur réseau : dans Node.js,res.json()est rejetée avecTypeError: terminated.
S'en prémunir dans le code
Lisez le corps comme du texte, décidez de ce que signifie un corps vide pour ce point d'accès, et n'analysez qu'ensuite. Cette approche traite au même endroit les réponses 204, HEAD et 200 vides, et garde un message d'erreur utile quand autre chose tourne mal :
async function readJson(res) {
const text = await res.text();
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${text.slice(0, 100) || '(empty body)'}`);
}
if (text.trim() === '') return null; // 204, HEAD ou un 200 vide
try {
return JSON.parse(text);
} catch (err) {
throw new Error(`Invalid JSON (${text.length} chars): ${err.message}`);
}
}
Pour les valeurs stockées, traitez de la même façon une valeur vide et une valeur absente, et revenez à une valeur par défaut :
function loadCart() {
const raw = localStorage.getItem('cart');
if (!raw) return []; // null (absente) ou '' (vide)
try {
return JSON.parse(raw);
} catch {
return []; // valeur abîmée : on repart de zéro
}
}
Côté serveur, soyez cohérent : renvoyez un 204 sans corps et sans type de contenu JSON, ou bien un petit document JSON comme {} ou {"deleted": true}. Une réponse 200 étiquetée application/json avec un corps vide, voilà la combinaison qui casse les clients.
Vérifier le texte à la main
Quand le corps n'est pas vide, copiez-le depuis le panneau Réseau ou vos journaux et collez-le dans le validateur JSON. Il utilise l'analyseur du navigateur lui-même : il affiche donc le même message et la même position que votre code, et formate le document dès qu'il est valide. Si la position signalée se trouve tout à la fin de ce que vous avez collé, cherchez la partie manquante du côté du transport ou du stockage plutôt que dans les données. Pour les erreurs qui citent un caractère précis, comme le < d'une page HTML, consultez Unexpected token in JSON.