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îtMessage
Chrome, Edge et Node.js, JSON.parseUnexpected 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-corsUnexpected end of input
FirefoxJSON.parse: unexpected end of data at line 1 column 1 of the JSON data
SafariJSON Parse error: Unexpected EOF
Python, json.loadsExpecting 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 DELETE ou à un PUT réussi par un statut 204 sans corps. res.ok vaut true pour un 204 : le vérifier ne protège donc pas l'appel res.json() qui suit.
  • Les requêtes HEAD. Une réponse à HEAD n'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', ''), un settings.json vide 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 parMessage dans les versions actuelles de Chrome et Node.js
{"id":Unexpected end of JSON input
{"tags":["a",Unexpected end of JSON input
{"ok":truUnexpected 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énement data de 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-Length et 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 avec TypeError: 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.

Questions Fréquentes

Que signifie « Unexpected end of JSON input » ?

L'analyseur a atteint la fin du texte alors qu'il attendait encore une valeur. Le plus souvent, le texte était vide, par exemple une réponse 204 ou une valeur stockée vide. Parfois, le document a été coupé en cours de route.

Pourquoi response.json() échoue-t-il alors que la requête a réussi ?

Une requête réussie n'implique pas la présence d'un corps. Une réponse 204 No Content a ok à true et aucun corps. Lisez le corps avec res.text() et ne l'analysez que s'il n'est pas vide.

JSON.parse(null) déclenche-t-il cette erreur ?

Non. null est converti en texte "null", qui est un JSON valide : JSON.parse(null) renvoie donc null. Une chaîne vide lève « Unexpected end of JSON input », et undefined lève une autre erreur.

Comment distinguer un corps vide d'un corps tronqué ?

Journalisez la longueur du texte avant l'analyse. Zéro signifie vide. Si la longueur est supérieure à zéro et que la position de l'erreur est égale à la longueur, le document a été coupé.