Unexpected Token in JSON: o que significa e como resolver

Esse erro significa que o texto que você tentou analisar não é JSON. O token citado na mensagem costuma revelar exatamente o que chegou no lugar dele.

Você chama JSON.parse() ou response.json() e, em vez dos dados, recebe um SyntaxError. O texto da mensagem muda de um navegador para outro, mas o significado é sempre o mesmo: o parser leu o texto desde o início e encontrou um caractere que não pode aparecer naquele ponto em um JSON válido. O caractere citado e a posição informada são as pistas mais úteis que você tem para descobrir o que aconteceu.

O mesmo erro em engines diferentes

As versões mais novas do Chrome e do Node.js mudaram a redação da mensagem, então você vai encontrar as duas formas nos resultados de busca e em logs mais antigos.

Onde apareceMensagem
Chrome, Edge, Node.js 20 e posterioresUnexpected token '<', "<!DOCTYPE "... is not valid JSON
Chrome antigo e Node.js 18Unexpected token < in JSON at position 0
FirefoxJSON.parse: unexpected character at line 1 column 1 of the JSON data
SafariJSON Parse error: Unrecognized token '<'

"Position 0" ou "line 1 column 1" quer dizer que o primeiro caractere já estava errado. Isso quase sempre indica que a resposta é outra coisa que não JSON, e não um pequeno erro de digitação dentro de um documento JSON.

A causa mais comum: uma página HTML no lugar do JSON

Um < inesperado na posição 0 é, de longe, a versão mais frequente. O código pediu JSON a uma API, mas o servidor respondeu com uma página HTML, e HTML começa com <!DOCTYPE html> ou <html>. Os motivos típicos são estes:

  • URL errada: um erro de digitação ou a falta do prefixo /api devolve a página 404 do site.
  • Erro no servidor: o framework renderiza uma página HTML de erro 500.
  • Sessão expirada: a requisição é redirecionada para uma página de login, e o fetch segue o redirecionamento sem avisar.
  • Fallback de single-page app: o servidor de desenvolvimento ou a hospedagem devolve index.html para qualquer caminho desconhecido, com status 200.
  • Um proxy, CDN ou firewall no meio do caminho envia a sua própria página HTML de erro ou de verificação.

O jeito mais rápido de confirmar é olhar a resposta bruta antes de fazer o parse:

const res = await fetch('/api/items');
console.log(res.status, res.headers.get('content-type'));
const text = await res.text();
console.log(text.slice(0, 200)); // shows the HTML if that is what came back

No navegador, você também pode abrir o painel Network (Rede) das ferramentas de desenvolvedor, clicar na requisição e conferir a aba Response. Se aparecer HTML, corrija a URL, a autenticação ou o erro do servidor: o código que faz o parse está certo.

Outros tokens e o que eles indicam

Mensagem atual (mensagem antiga)O que você tentou analisarCorreção
"undefined" is not valid JSON (Unexpected token u in JSON at position 0)O valor undefined, por exemplo uma chave que não existe no localStorage ou uma variável sem valorConfira se o valor existe antes de analisá-lo
"[object Object]" is not valid JSON (Unexpected token o in JSON at position 1)Um objeto que já tinha sido analisado, transformado em string pelo próprio JSON.parseUse o objeto diretamente; não faça o parse duas vezes
Expected property name or '}' in JSON at position 1Aspas simples ou nomes de propriedade sem aspas, como em {'a': 1} ou {a: 1}Use aspas duplas em todos os nomes de propriedade e em todas as strings
Expected double-quoted property name in JSON at position 8Uma vírgula sobrando no final, como em {"a": 1,}Remova a vírgula antes da chave que fecha o objeto
Unexpected token 'N', "{"a": NaN}" is not valid JSONNaN, Infinity ou undefined dentro dos dadosSubstitua por null ou por um número
Uma mensagem que cita um token invisível ou aparentemente vazio na posição 0Uma marca de ordem de bytes (BOM) no início de um arquivo salvo por alguns editores do WindowsSalve o arquivo como UTF-8 sem BOM ou remova o primeiro caractere

Fazer o parse de dados que já foram analisados

Bibliotecas costumam entregar objetos prontos. response.json() já devolve um objeto JavaScript, e o axios faz o mesmo em response.data. Passar esse objeto para JSON.parse faz com que ele seja convertido antes na string "[object Object]", que falha na posição 1. A solução é remover o parse extra:

const data = await res.json();
// JSON.parse(data)  <- fails: data is already an object
console.log(data.items);

Encontrar o erro dentro de um JSON de verdade

Quando a posição do erro fica em algum ponto no meio do texto, ele é mesmo JSON, só que com um erro. Cole o conteúdo no validador de JSON: ele usa o mesmo parser do seu navegador, mostra a mensagem com a posição e a linha e formata o documento assim que ele fica válido. O erro real costuma estar logo antes da posição informada, como uma vírgula faltando entre dois itens ou uma aspa que deveria ser dupla.

Como se proteger no seu código

Conferir o status e o tipo de conteúdo antes do parse transforma um erro de sintaxe confuso em uma mensagem que diz o que realmente aconteceu:

async function getJson(url) {
  const res = await fetch(url);
  const type = res.headers.get('content-type') || '';
  if (!res.ok || !type.includes('application/json')) {
    const body = await res.text();
    throw new Error(`Expected JSON from ${url}, got ${res.status} ${type}: ${body.slice(0, 100)}`);
  }
  return res.json();
}

A mesma ideia vale do lado do servidor: faça as rotas de API devolverem corpos de erro em JSON, e não páginas HTML de erro, e envie junto o cabeçalho Content-Type: application/json. Para a mensagem parecida que aparece quando o corpo da resposta está vazio ou cortado, veja o guia Unexpected end of JSON input.

Perguntas Frequentes

O que significa "Unexpected token < in JSON at position 0"?

A resposta começou com o caractere <, ou seja, é HTML, normalmente uma página de erro, uma página de login ou um index.html de fallback, e não JSON. Confira a URL da requisição, o código de status e o cabeçalho Content-Type.

Por que a mensagem diz que "[object Object]" is not valid JSON?

Você passou um objeto para o JSON.parse, e ele foi convertido primeiro no texto [object Object]. O valor já está analisado, então use-o diretamente.

Um validador de JSON consegue encontrar o problema?

Sim, se o texto deveria ser JSON. Um validador mostra a posição exata do primeiro erro. Se o texto for HTML, a correção está no servidor ou na requisição, não no JSON.