SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON —— quase sempre significa uma coisa: seu código chamou JSON.parse() (ou res.json()) em uma página HTML, não JSON. O < na posição 0 é a abertura de <!DOCTYPE html> ou <html>. Aqui está exatamente por que isso acontece e como consertar.
Como o erro aparece
// V8 (Chrome / Node / Edge)
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
// V8 mais antigo
SyntaxError: Unexpected token < in JSON at position 0
// Firefox
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
// Safari
SyntaxError: JSON Parse error: Unexpected identifier "<"Posição 0 é a pista chave: o primeiro byte já está errado, então a resposta nunca foi JSON desde o começo.
Por que acontece: você recebeu HTML
Uma chamada fetch ou AJAX devolveu um documento HTML em vez do JSON esperado. Origens comuns:
- Uma página de erro 404 / 500 servida como HTML pelo seu framework ou servidor web
- Um redirecionamento de login ou auth para uma página HTML (sessão expirada)
- Uma URL errada —— batendo na rota do app/SPA em vez do endpoint da API
- Um proxy, CDN ou captive portal devolvendo sua própria página HTML
Exemplo quebrado
const res = await fetch('/api/user'); // servidor devolveu uma página HTML 404
const data = await res.json(); // ❌ Unexpected token '<' …
// res.json() tenta parsear "<!DOCTYPE html>…" como JSONExemplo consertado
const res = await fetch('/api/user');
// 1) Cheque o status antes de parsear
if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}
// 2) Opcional: confirme que é realmente JSON
const type = res.headers.get('content-type') ?? '';
if (!type.includes('application/json')) {
const text = await res.text();
throw new Error(`Expected JSON, got: ${text.slice(0, 80)}…`);
}
const data = await res.json(); // ✅ seguroDuas origens menos óbvias que vale conhecer
- Respostas bloqueadas por WAF ou rate-limit. Um WAF, CDN ou camada de proteção DDoS pode interceptar a chamada e devolver sua própria página HTML de desafio / bloqueio —— o interstitial do Cloudflare, a página de negação do AWS WAF, a parede de captive-portal de Wi-Fi público. O status geralmente é
403/429/503com corpo HTML. Inspecione a resposta na aba Network e veja se há headers comoCF-RayouX-Amz-Cf-Idpara detectar. - O fallback de history da SPA devolvendo
index.html. Hosts estáticos e dev-servers frequentemente reescrevem qualquer rota desconhecida para/index.htmlpara que o router do cliente assuma. Se seu front-end por acidente fizer fetch de/api/usercomo caminho relativo e o proxy não estiver configurado, o servidor silenciosamente devolve a casca HTML da sua SPA com200—— e você acaba com uma versão confusa de „200 OK mas é HTML" do mesmo erro.
Como consertar —— passo a passo
- Logue o body cru:
console.log(await res.clone().text()). Se começa com<!DOCTYPEou<html>, é HTML. - Cheque
res.ok/res.statusantes de chamarres.json()—— um 4xx/5xx tipicamente devolve uma página HTML de erro. - Verifique a URL na aba Network. Um caminho relativo pode resolver para o index.html da sua SPA em vez da API.
- Cheque a autenticação —— um redirecionamento para a página de login significa que seu token/sessão expirou.
- Inspecione
Content-Type—— ramifique sobre ele para lidar com respostas não-JSON em vez de parseá-las às cegas.
Perguntas frequentes
O que significa „Unexpected token < in JSON at position 0"?
O parser encontrou < como primeiro caractere, o que nunca é JSON válido. A resposta era HTML (uma página <!DOCTYPE> ou <html>), não o JSON que seu código esperava.
Por que meu fetch retorna HTML em vez de JSON?
Causas comuns: página de erro 404/500, redirecionamento de auth para uma página de login, ou URL errada que resolve para seu front-end em vez da API. Cheque res.ok e logue await res.text() para ver o que voltou.
Como evito que isso quebre meu app?
Guard antes de parsear: cheque res.ok e o header Content-Type, e envolva JSON.parse()/res.json() em try/catch para que uma resposta HTML vire um erro tratado em vez de uma exceção.
Conserte agora
Se você tem um body de resposta sobre o qual está em dúvida, cole no JSON Fix —— ele te diz instantaneamente se é JSON válido ou outra coisa (como HTML). Tudo roda no seu navegador.
- JSON Fix —— valide e repare JSON no seu navegador
- Como consertar erros „Unexpected Token" do JSON.parse —— cada variante de token
- Unexpected token u in JSON at position 0 —— o erro gêmeo do
undefined - Consertar „[object Object] is not valid JSON" —— a referência completa de erros de sintaxe