O erroSyntaxError: Token inesperado ‘<‘ , “<!DOCTYPE”… não é JSON válido (ou “Token inesperado em JSON na posição 0”) significa que você tentou analisar algo que não é JSON — geralmente uma página de erro HTML ou uma resposta vazia. Veja como diagnosticar e corrigir isso.
📋 Table of Contents
- O que esse erro significa
- Causa 1: o servidor retornou uma página de erro HTML
- Causa 2: Corpo de resposta vazio
- Causa 3: URL / endpoint de API errado
- Causa 4: JSON já analisado com análise dupla
- Causa 5: caracteres finais ou JSON inválido
- O padrão de busca robusto
- Etapas de depuração
- Perguntas Frequentes
- Conclusão
O que esse erro significa
Você ligouJSON.parse() or response.json() em uma resposta que não é JSON válida. As pistas “posição 0” e “<” são fundamentais: posição 0 significa que o primeiro caractere está errado e “<” significa que a resposta começa com HTML (como<!DOCTYPE html>) — você obteve uma página de erro HTML em vez de JSON.
Causa 1: o servidor retornou uma página de erro HTML
// 🐛 The API returned a 404/500 HTML page, not JSON
const response = await fetch('/api/data');
const data = await response.json(); // ❌ fails - response is HTML
// The response body is something like:
//
404 Not Found ...
// JSON.parse chokes on the '<' at position 0
// ✅ Check the response status and content type first
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
if (!contentType?.includes('application/json')) {
const text = await response.text();
throw new Error(`Expected JSON but got: ${text.slice(0, 100)}`);
}
const data = await response.json();
Causa 2: Corpo de resposta vazio
// 🐛 Parsing an empty response (e.g., 204 No Content)
const data = await response.json(); // ❌ fails if body is empty
// ✅ Handle empty bodies
const text = await response.text();
const data = text ? JSON.parse(text) : null;
// ✅ Or check status
if (response.status === 204) {
return null; // No Content - nothing to parse
}
const data = await response.json();
Causa 3: URL / endpoint de API errado
// 🐛 Wrong URL returns your app's HTML (SPA fallback) instead of API JSON
fetch('/api/users') // typo or wrong path
// If /api/users doesn't exist, the server may return index.html
// (common with SPAs) - which starts with '<' -> parse error
// ✅ Verify the URL is correct and the endpoint exists
// Check the Network tab to see what the response actually is
// Ensure API routes are registered and the path is exact
Causa 4: JSON já analisado com análise dupla
// 🐛 The data is already an object, but you parse it again
const data = await response.json(); // already parsed to an object
const parsed = JSON.parse(data); // ❌ parsing an object fails
// ✅ response.json() already parses - don't parse again
const data = await response.json(); // done - it's an object
// axios also auto-parses:
const { data } = await axios.get('/api'); // data is already parsed
Causa 5: caracteres finais ou JSON inválido
// 🐛 The JSON has a syntax error (trailing comma, single quotes)
JSON.parse('{"name": "Alice",}'); // ❌ trailing comma
JSON.parse("{'name': 'Alice'}"); // ❌ single quotes not valid JSON
JSON.parse('{name: "Alice"}'); // ❌ unquoted key
// ✅ Valid JSON requires double quotes, no trailing commas
JSON.parse('{"name": "Alice"}'); // ✓ valid
O padrão de busca robusto
async function fetchJSON(url, options) {
const response = await fetch(url, options);
// Check status
if (!response.ok) {
const text = await response.text();
throw new Error(`HTTP ${response.status}: ${text.slice(0, 200)}`);
}
// Handle empty responses
const text = await response.text();
if (!text) return null;
// Parse safely with a helpful error
try {
return JSON.parse(text);
} catch (err) {
throw new Error(`Invalid JSON response: ${text.slice(0, 200)}`);
}
}
// Usage
try {
const data = await fetchJSON('/api/users');
} catch (err) {
console.error('Fetch failed:', err.message); // clear error, not cryptic
}
Etapas de depuração
// 1. Log the raw response text to see what you actually got
const response = await fetch(url);
const text = await response.text();
console.log('Raw response:', text); // reveals HTML, empty, or malformed JSON
// 2. Check the Network tab in DevTools
// - Status code (200? 404? 500?)
// - Content-Type header (application/json?)
// - Response body (is it actually JSON?)
// 3. Test the endpoint directly
curl -i https://api.example.com/data
// See the exact status, headers, and body
Perguntas Frequentes
P: Por que o erro menciona “<” ou “<!DOCTYPE”?
R: Porque a resposta começa com HTML, não com JSON. Você obteve uma página HTML (geralmente uma página de erro 404 ou 500, ou index.html do seu SPA) em vez do JSON esperado. O “<” é o primeiro caractere da tag HTML, que não é JSON válido.
P: Por que funciona algumas vezes, mas outras não?
R: Ele falha quando o servidor retorna não-JSON — uma página de erro (404/500), um corpo vazio ou um redirecionamento para HTML. Funciona quando a API retorna JSON adequado. A falha intermitente geralmente significa que a API às vezes apresenta erros e retorna páginas de erro HTML.
P: Como evito esse erro?
R: Sempre verifiqueresponse.ok e o tipo de conteúdo antes de chamarresponse.json(), manipule corpos vazios e envolva a análise em try/catch. Use um wrapper de busca robusto (mostrado acima) que forneça erros claros em vez da mensagem enigmática de “token inesperado”.
P: Estou recebendo o HTML do meu aplicativo em vez dos dados da API. Por que?
R: Provavelmente o URL da sua API está errado ou o endpoint não existe, então o servidor volta a servir o index.html do seu SPA (que começa com <!DOCTYPE). Verifique se o caminho da API está correto e se o endpoint está registrado. Verifique a guia Rede para ver a resposta real.
P: Devo usar response.json() ou response.text() e depois JSON.parse()?
A: response.json() é conveniente, mas gera um erro enigmático em JSON inválido. Usandoresponse.text() entãoJSON.parse() em um try/catch permite inspecionar a resposta bruta e fornecer uma mensagem de erro clara quando não for JSON – melhor para depuração.
Conclusão
“SyntaxError: Token inesperado em JSON na posição 0” significa que você tentou analisar não JSON – quase sempre uma página de erro HTML (começa com “<“) ou uma resposta vazia. A correção:verificarresponse.ok e o cabeçalho do tipo de conteúdo antes de analisar, manipule corpos vazios e envolva JSON.parse em try/catch. A pista “<” informa que a resposta é HTML, então investigue o motivo – geralmente um URL errado, um erro do servidor retornando uma página de erro HTML ou um substituto de SPA. Use um wrapper de busca robusto que registre a resposta bruta e forneça erros claros. Depois de verificar a resposta antes de analisá-la, esse erro enigmático se torna informativo e aponta diretamente para o problema real.
🔗 Share this article
✍️ Leave a Comment