Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Il tuo codice ha chiamato JSON.parse (o res.json()) su un documento HTML. La parentesi angolare in posizione 0 è l’apertura di <!DOCTYPE html>. Il JSON non è malformato; semplicemente non hai ricevuto JSON. Non c’è nulla da sistemare nel parser. È la richiesta a dover essere sistemata.
Potresti vederlo scritto anche così
- Unexpected token < in JSON at position 0
- Unexpected token '<', "<html><hea"... is not valid JSON
- SyntaxError: Unexpected token < in JSON at position 0
V8 ha riscritto quasi tutti i suoi messaggi di errore JSON nel 2022. La formulazione più vecchia resta quella che mostrano quasi tutti i risultati di ricerca, ma nessun runtime attuale la emette.
Incolla il tuo JSON e vedi esattamente dove si rompe
Nulla di ciò che incolli lascia il tuo browser. L’allowlist connect-src ne fa una garanzia del browser anziché una promessa. Verificalo tu stesso
Che cosa lo provoca davvero
In ordine di quanto spesso ciascuno si rivela essere la risposta.
-
01 Il server ha restituito una pagina di errore
Un 404, un 500 o un errore di gateway reso in HTML. Molti framework restituiscono pagine di errore in HTML anche per le rotte di API, a meno che tu non chieda esplicitamente JSON. Controlla prima il codice di stato, non il corpo.
Si rompe
const data = await fetch('/api/user').then((r) => r.json());Funziona
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 Una single-page app ha riscritto un percorso sconosciuto verso index.html
È la causa più comune in sviluppo. Vite, Create React App e quasi tutti i server di sviluppo servono index.html per qualsiasi percorso che non riconoscono, quindi un refuso in una rotta di API o un proxy non configurato restituisce la tua stessa applicazione come HTML con stato 200. Ecco perché controllare res.ok da solo non basta.
-
03 La URL si è risolta in un posto inatteso
Un percorso relativo come fetch("api/user") si risolve rispetto alla rotta corrente, quindi da /settings/profile richiede /settings/api/user. Usa una barra iniziale o una URL assoluta.
Si rompe
fetch('api/user')Funziona
fetch('/api/user') -
04 Un captive portal, una VPN o un proxy ha intercettato la richiesta
Il wifi di un albergo, un proxy aziendale o una challenge anti-bot di Cloudflare restituisce la propria pagina HTML. Si presenta come un errore che colpisce solo alcuni utenti, e solo su certe reti.
-
05 PHP ha stampato un warning prima del JSON
Una notice o un warning emessi prima del corpo della risposta fanno iniziare il documento con <br /> anziché con {. Il JSON è ancora lì, dopo l’HTML. Disattiva display_errors in produzione.
Lo stesso errore in altri runtime
Il problema di fondo è identico; cambia solo la formulazione. Se un collega segnala uno di questi messaggi, sta guardando esattamente la stessa cosa che vedi tu.
| 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 |
Domande frequenti
- Perché il messaggio è cambiato rispetto a «Unexpected token < in JSON at position 0»?
- V8 ha riscritto i suoi messaggi di errore JSON nel 2022 per includere uno spezzone dell’input incriminato. Chrome 104 e successivi, e Node 18 e successivi, stampano la nuova formulazione. La vecchia stringa resta quella che mostrano quasi tutti i risultati di ricerca e le risposte su Stack Overflow, ma nessun runtime attuale la emette.
- Il mio JSON è rotto?
- No. Non c’è alcun JSON di mezzo. Il parser sta leggendo un documento HTML. Sistema ciò che restituisce la richiesta e l’errore sparisce senza toccare il tuo JSON.
- Il codice di stato è 200, allora perché è HTML?
- Un server di sviluppo che ripiega su index.html per le rotte sconosciute restituisce 200 con un corpo HTML. Controlla l’header Content-Type oltre allo stato.
Risolvilo adesso
Incolla il payload nello strumento qui sopra, oppure vai direttamente a quello creato per questo compito.
Controlla un payload