← Todos os artigos

Como corrigir erros «Unexpected Token» do JSON.parse

«Unexpected token '<'» ou «Unexpected token u in JSON at position 0» — esses erros param seu app na hora. Eis o que cada variação quer dizer e exatamente como corrigir.

Você está encarando uma stack trace vermelha: SyntaxError: Unexpected token '<', "..." is not valid JSON. Ou talvez seja Unexpected token u in JSON at position 0. De qualquer jeito, JSON.parse() se recusou a rodar e seu código parou. Este guia explica exatamente o que cada variante desse erro significa, por que acontece, e como consertar —— rápido.

Como o erro aparece de verdade

A redação varia por engine JavaScript, mas todas significam a mesma coisa: o parser bateu num caractere que não esperava numa posição específica da sua string.

// V8 (Chrome, Node.js, Edge)
SyntaxError: Unexpected token '<', "<html>..." is not valid JSON
SyntaxError: Unexpected token 'u', "undefined" is not valid JSON
SyntaxError: Unexpected token u in JSON at position 0  // V8 mais antigo
SyntaxError: Expected ',' or '}' after property value in JSON at position 42

// Firefox (SpiderMonkey)
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data

// Safari (JavaScriptCore)
SyntaxError: JSON Parse error: Unexpected identifier "undefined"
SyntaxError: JSON Parse error: Single quotes (') are not allowed in JSON

A informação chave está sempre em duas partes: que caractere foi inesperado e onde. O caractere te diz o que o parser efetivamente recebeu; a posição te diz onde olhar na string.

Causa 1: o servidor devolveu HTML em vez de JSON

Essa é a causa mais comum de Unexpected token '<'. Seu fetch ou chamada AJAX recebeu uma página HTML —— normalmente uma 404, um redirect de login, ou um erro de servidor —— em vez do JSON esperado. O < é a abertura de <!DOCTYPE html> ou <html>.

// ❌ Isso quebra com "Unexpected token '<'"
const res  = await fetch('/api/user');
const data = await res.json(); // servidor devolveu HTML 404

// ✅ Cheque o status da resposta primeiro
const res = await fetch('/api/user');
if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
const data = await res.json();

Sempre cheque res.ok (ou res.status) antes de chamar res.json(). Você também pode inspecionar o texto cru da resposta primeiro:

const text = await res.text();
console.log(text.slice(0, 200)); // veja o que de fato veio
const data = JSON.parse(text);

Causa 2: o valor é undefined

Unexpected token 'u' na posição 0 quase sempre significa que você chamou JSON.parse(undefined). A string "undefined" começa com a letra u, que não é um caractere de início válido de JSON.

// ❌ variável nunca foi setada
const raw = localStorage.getItem('settings'); // devolve null se ausente
JSON.parse(raw);  // null tudo bem, mas se raw for undefined por algum motivo...

// ❌ dado async ainda não carregado
JSON.parse(this.state.data); // data é undefined no primeiro render

// ✅ Guard antes de parsear
if (raw) {
  const data = JSON.parse(raw);
}

Note que localStorage.getItem() devolve null (não undefined) para keys ausentes, e JSON.parse(null) devolve null sem erro. Os suspeitos comuns são variáveis de state não inicializadas ou um argumento de função ausente.

Causa 3: aspas simples em vez de duplas

// ❌ SyntaxError: Unexpected token '''
JSON.parse("{'name': 'Ada'}");

// ✅ JSON requer aspas duplas
JSON.parse('{"name": "Ada"}')

Literais de objeto JavaScript aceitam aspas simples. JSON não —— todas as strings, incluindo as keys, têm que usar aspas duplas. Isso costuma acontecer quando alguém serializa dado com a função str() do Python em vez de json.dumps(), ou copia um literal de objeto direto.

Causa 4: vírgula no final

// ❌ SyntaxError: Unexpected token '}'
JSON.parse('{"name": "Ada", "score": 98,}');
//                                       ^ vírgula no final

// ✅
JSON.parse('{"name": "Ada", "score": 98}')

JavaScript moderno permite vírgulas no final em arrays e objetos. JSON não. Arquivos de configuração JSON editados à mão são a fonte mais comum desse erro.

Causa 5: keys sem aspas

// ❌ SyntaxError: Unexpected token 'n'
JSON.parse("{name: 'Ada'}");

// ✅
JSON.parse('{"name": "Ada"}')

As keys de objeto em JSON precisam sempre ser strings entre aspas duplas. Identificadores nus tipo name são válidos em literais de objeto JavaScript mas não em JSON.

Causa 6: comentários no JSON

// ❌ SyntaxError: Unexpected token '/'
JSON.parse(`{
  // registro do usuário
  "name": "Ada"
}`);

// ✅ Tire os comentários antes de parsear (ou use um parser JSONC)
const stripped = raw.replace(///.*$/gm, '').replace(//*[sS]*?*//g, '');
JSON.parse(stripped);

JSON não tem sintaxe de comentários. Comentários são frequentemente adicionados a arquivos de configuração (o formato JSONC), mas o JSON.parse() padrão rejeita na hora.

Causa 7: um BOM ou caractere invisível na posição 0

Se o erro diz posição 0 mas o primeiro caractere visível parece tranquilo, pode ter um byte-order mark invisível (BOM, U+FEFF) ou outro caractere de controle no início da string —— tipicamente ao ler um arquivo salvo com codificação UTF-8-BOM no Windows.

// ✅ Tire o BOM antes de parsear
const clean = raw.replace(/^/, '');
JSON.parse(clean);

Causa 8: tokens I ou N —— Infinity e NaN

Se o erro diz Unexpected token I ou Unexpected token N, a fonte produziu Infinity, -Infinity, ou NaN —— todos válidos em JavaScript mas não em JSON.

// ❌ Erro do lado produtor
const json = '{"ratio": NaN, "limit": Infinity}';
JSON.parse(json); // Unexpected token N (or I) ...

// ✅ Codifique deliberadamente
const safe = JSON.stringify({ ratio: NaN, limit: Infinity });
// → '{"ratio":null,"limit":null}'  (nota: com perda —— ambos viram null)

// ✅ Sem perda: codifique como uma string sentinela que você parseia de volta
JSON.stringify({ ratio: 'NaN', limit: 'Infinity' });

JSON.stringify converte silenciosamente NaN / Infinity para null. Se você precisa que façam round-trip, substitua por uma string sentinela na saída e parseie de volta na entrada.

Como achar a posição exata do erro

A mensagem de erro inclui um número de posição. Use ele pra fatiar a string e ver exatamente o que tem ali:

function parseWithContext(text) {
  try {
    return JSON.parse(text);
  } catch (err) {
    // Extrai a posição da mensagem de erro do V8
    const match = err.message.match(/position (\d+)/);
    if (match) {
      const pos = Number(match[1]);
      const snippet = text.slice(Math.max(0, pos - 20), pos + 20);
      console.error(`Error near: ..."${snippet}"...`);
      console.error(`             ${''.padStart(Math.min(pos, 20), ' ')}^`);
    }
    throw err;
  }
}

Perguntas frequentes

O que significa «Unexpected token in JSON»?

Significa que JSON.parse() bateu num caractere que não é válido naquele ponto da gramática. O token nomeado te diz o que foi recebido (< = HTML, u = undefined, ' = aspas simples) e a posição te diz onde olhar.

Por que «Unexpected token '<'» acontece com fetch?

Sua requisição devolveu uma página HTML —— um 404, um redirect de login, ou erro de servidor —— em vez de JSON, e o < é a tag HTML de abertura. Cheque response.ok antes de chamar response.json() e logue await response.text() pra ver o que de fato voltou.

Como consertar «Unexpected token u in JSON at position 0»?

Você passou undefined para JSON.parse(). Coloque um guard no valor antes (if (raw) JSON.parse(raw)) e cheque state não inicializado ou argumento de função ausente. A variante intimamente relacionada token o é tratada em Unexpected token o in JSON.

Como acho a posição exata do erro?

Leia o número de posição na mensagem de erro e fatie a string em volta (veja o snippet acima), ou cole o JSON no JSON Fix, que destaca a linha exata do problema.

O conserto mais rápido: cole e repare online

Se você tem uma string JSON quebrada e só quer que ela seja consertada agora, o JSON Fix lida com todas as causas acima automaticamente —— aspas simples, vírgulas no final, keys sem aspas, comentários, literais Python e mais. Cole seu JSON, clique Repair & Format, e tenha JSON válido e limpo de volta num passo. Tudo roda no seu navegador; nada é enviado pra qualquer servidor.