Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Votre code a appelé JSON.parse (ou res.json()) sur un document HTML. Le chevron en position 0 est l’ouverture de <!DOCTYPE html>. Le JSON n’est pas malformé ; vous n’avez tout simplement pas reçu de JSON. Rien n’est à corriger dans l’analyseur. C’est la requête qui l’est.
Vous pouvez aussi le voir écrit ainsi
- Unexpected token < in JSON at position 0
- Unexpected token '<', "<html><hea"... is not valid JSON
- SyntaxError: Unexpected token < in JSON at position 0
V8 a réécrit la plupart de ses messages d’erreur JSON en 2022. L’ancienne formulation reste ce que montrent la plupart des résultats de recherche, mais aucun runtime actuel ne l’émet.
Collez votre JSON et voyez exactement où il casse
Rien de ce que vous collez ne quitte votre navigateur. La liste d’autorisation connect-src en fait une garantie du navigateur plutôt qu’une promesse. Vérifiez-le vous-même
Ce qui le provoque réellement
Classés selon la fréquence à laquelle chacun se révèle être la réponse.
-
01 Le serveur a renvoyé une page d’erreur
Un 404, un 500 ou une erreur de passerelle rendue en HTML. Beaucoup de frameworks renvoient des pages d’erreur HTML même pour des routes d’API, sauf si vous demandez explicitement du JSON. Vérifiez d’abord le code de statut, pas le corps.
Casse
const data = await fetch('/api/user').then((r) => r.json());Fonctionne
const res = await fetch('/api/user', { headers: { Accept: 'application/json' } }); if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); const type = res.headers.get('content-type') ?? ''; if (!type.includes('application/json')) { throw new Error(`Expected JSON, got ${type}: ${(await res.text()).slice(0, 200)}`); } const data = await res.json(); -
02 Une application monopage a réécrit un chemin inconnu vers index.html
C’est la cause la plus fréquente en développement. Vite, Create React App et la plupart des serveurs de développement servent index.html pour tout chemin qu’ils ne reconnaissent pas : une faute de frappe dans une route d’API ou un proxy mal réglé renvoie donc votre propre application en HTML avec un statut 200. C’est pourquoi vérifier res.ok ne suffit pas à soi seul.
-
03 L’URL s’est résolue à un endroit inattendu
Un chemin relatif comme fetch("api/user") se résout par rapport à la route actuelle : depuis /settings/profile, il demande /settings/api/user. Utilisez une barre oblique initiale ou une URL absolue.
Casse
fetch('api/user')Fonctionne
fetch('/api/user') -
04 Un portail captif, un VPN ou un proxy a intercepté la requête
Le wifi d’un hôtel, un proxy d’entreprise ou un challenge anti-bot Cloudflare renvoie sa propre page HTML. Cela se manifeste par une erreur que seuls certains utilisateurs rencontrent, et seulement sur certains réseaux.
-
05 PHP a affiché un avertissement avant le JSON
Une notice ou un warning émis avant le corps de la réponse fait commencer le document par <br /> plutôt que par {. Le JSON est toujours là, après le HTML. Désactivez display_errors en production.
La même erreur dans d’autres environnements
Le problème de fond est identique ; seule la formulation change. Si un collègue signale l’un de ces messages, il regarde exactement la même chose que vous.
| Python | Expecting value: line 1 column 1 (char 0) |
|---|---|
| C# (Newtonsoft) | Unexpected character encountered while parsing value: <. Path '', line 0, position 0. |
| C# (System.Text.Json) | '<' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0. |
| Java (Jackson) | Unexpected character ('<' (code 60)): expected a valid value |
Questions fréquentes
- Pourquoi le message a-t-il changé par rapport à « Unexpected token < in JSON at position 0 » ?
- V8 a réécrit ses messages d’erreur JSON en 2022 pour y inclure un extrait de l’entrée fautive. Chrome 104 et suivants, ainsi que Node 18 et suivants, affichent la nouvelle formulation. L’ancienne chaîne reste ce que montrent la plupart des résultats de recherche et des réponses Stack Overflow, mais aucun runtime actuel ne l’émet.
- Mon JSON est-il cassé ?
- Non. Il n’y a aucun JSON dans l’histoire. L’analyseur lit un document HTML. Corrigez ce que renvoie la requête et l’erreur disparaît sans toucher à votre JSON.
- Le code de statut est 200, alors pourquoi est-ce du HTML ?
- Un serveur de développement qui bascule vers index.html pour les routes inconnues renvoie 200 avec un corps HTML. Vérifiez l’en-tête Content-Type autant que le statut.
Corrigez-le maintenant
Collez le payload dans l’outil ci-dessus, ou allez directement à celui conçu pour ce travail.
Vérifier un payload