Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

Tipos de erro em JavaScript e como ler o console

Os sete erros nativos da linguagem, o que cada mensagem quer dizer e como ler uma stack trace de cima para baixo — com cada erro reproduzido no Node 24.

Rodolfo Mori9 min de leitura

Todo erro em JavaScript é um objeto com três informações: name (a família do erro), message (a descrição) e stack (o caminho de chamadas até o ponto da falha). Aprender a ler essas três coisas é o que separa “deu erro, não sei o que é” de “quebrou na linha 42 porque a API não devolveu entrega”.

A resposta curta: existem sete tipos nativos, e na prática você vai encontrar quatro deles. SyntaxError acontece antes de o arquivo rodar, ReferenceError é nome que não existe, TypeError é valor que não serve para o que você pediu e RangeError é número ou recursão fora do limite. O resto deste texto mostra cada um rodando, e ensina a ler a stack trace que vem junto.

Os exemplos são de uma loja virtual: pedido, frete, cupom, gateway de pagamento.

Leia o erro como uma ficha de atendimento

Quando um aparelho vai para assistência, a ficha separa o tipo do defeito, a descrição e o caminho percorrido até a bancada. Um erro JavaScript entrega as mesmas pistas: name classifica a família, message descreve o que falhou e stack lista as chamadas que trouxeram o programa até ali.

Essa comparação não quer dizer que a mensagem já conhece a causa. Ela informa o sintoma no ponto da quebra. Cabe a você voltar pelo caminho e descobrir quem entregou o valor errado. Antes de ler cada exemplo, faça um treino de dez segundos: encontre o nome, sublinhe o valor citado na mensagem e localize o primeiro arquivo seu na stack. Só depois olhe o código. Esse roteiro transforma um paredão vermelho em três perguntas pequenas e evita começar a depuração pela última linha aleatória do console.

Os sete nomes que a linguagem usa

Todos herdam de Error, e é por isso que erro instanceof Error pega qualquer um deles:

js
const nativos = [
  EvalError, RangeError, ReferenceError,
  SyntaxError, TypeError, URIError, AggregateError,
];

for (const Tipo of nativos) {
  const instancia = Tipo === AggregateError ? new Tipo([], 'x') : new Tipo('x');
  console.log(
    Tipo.name.padEnd(16),
    'herda de Error:', instancia instanceof Error,
    '| protótipo:', Object.getPrototypeOf(Tipo.prototype).constructor.name,
  );
}
EvalError herda de Error: true | protótipo: Error RangeError herda de Error: true | protótipo: Error ReferenceError herda de Error: true | protótipo: Error SyntaxError herda de Error: true | protótipo: Error TypeError herda de Error: true | protótipo: Error URIError herda de Error: true | protótipo: Error AggregateError herda de Error: true | protótipo: Error
tipo quando aparece frequência real
SyntaxError o motor não conseguiu entender o arquivo, ou o JSON.parse recebeu texto quebrado altíssima
ReferenceError você usou um nome que não existe em nenhum escopo visível alta
TypeError o valor existe mas não serve: null.x, undefined.y, x is not a function altíssima
RangeError número fora do intervalo aceito, array de tamanho inválido, recursão infinita média
URIError decodeURIComponent ou encodeURI receberam uma sequência de escape malformada baixa
AggregateError vários erros de uma vez — na prática, só o Promise.any produz baixa
EvalError nunca, em código moderno: sobrou por compatibilidade com o eval antigo zero

Quatro deles em uma tacada só, cada um provocado de propósito:

js
const tentativas = [
  () => cupomDeDesconto,
  () => null.total,
  () => (289.9).toFixed(105),
  () => decodeURIComponent('%BR'),
  () => JSON.parse('{"total": }'),
];

for (const executar of tentativas) {
  try {
    executar();
  } catch (erro) {
    console.log(erro.name.padEnd(15), '|', erro.message);
  }
}
ReferenceError | cupomDeDesconto is not defined TypeError | Cannot read properties of null (reading 'total') RangeError | toFixed() digits argument must be between 0 and 100 URIError | URI malformed SyntaxError | Unexpected token '}', "{"total": }" is not valid JSON

O último merece um comentário: JSON.parse lança SyntaxError em tempo de execução, e por isso ele pode ser capturado por um try/catch. O SyntaxError do próximo tópico é outra história.

O erro que acontece antes de rodar

SyntaxError de arquivo não é capturável e não deixa nem a primeira linha executar. O motor precisa entender o arquivo inteiro antes de rodar qualquer coisa dele:

js
console.log('primeira linha');

const carrinho = { itens: ['Teclado mecânico'];
/private/tmp/loja/e02.mjs:3 const carrinho = { itens: ['Teclado mecânico']; ^ SyntaxError: Unexpected token ';' at checkSyntax (node:internal/main/check_syntax:72:5) Node.js v24.16.0

Repare no que não apareceu: a linha primeira linha. Se você espera um log no começo do arquivo e ele some, essa é a pista de que o problema é sintático, e não lógico.

O Unexpected token ';' aqui é enganoso: o ponto e vírgula está certo, o que falta é o } que deveria vir antes dele. O motor reclama do primeiro caractere que não cabe na gramática, não do lugar onde você errou — quase sempre a chave, o parêntese ou a aspa esquecida está antes do cursor ^.

Unexpected end of input

O primo desse erro é o mais frustrante de todos, porque a linha apontada é sempre a última do arquivo — que costuma estar impecável:

js
function calcularTotal(itens) {
  let total = 0;
  for (const item of itens) {
    total += item.preco * item.quantidade;
  }
  return total;

console.log(calcularTotal([{ preco: 289.9, quantidade: 1 }]));
/private/tmp/loja/e03.mjs:9 SyntaxError: Unexpected end of input at checkSyntax (node:internal/main/check_syntax:72:5) Node.js v24.16.0

Traduzindo: “o arquivo acabou e eu ainda estava esperando alguma coisa”. Falta o } que fecha calcularTotal. O motor só descobre isso no fim, porque até lá o console.log da linha 8 era um candidato perfeitamente válido a corpo de função.

Como caçar: pelo recuo. Salve o arquivo com o formatador ligado (Prettier no salvar) e o bloco que não fecha vai se denunciar sozinho, empurrando o resto do código para a direita.

Nome que não existe contra valor que não serve

Essa dupla responde por boa parte dos erros do dia a dia, e a diferença entre elas é o primeiro diagnóstico a fazer:

js
const carrinho = { cliente: 'Ana' };

console.log('propriedade que não existe:', carrinho.cupom);
console.log('typeof em nome inexistente:', typeof cupomGlobal);

try {
  console.log(cupomGlobal);
} catch (erro) {
  console.log(erro.name, '|', erro.message);
}

try {
  carrinho.cupom.codigo;
} catch (erro) {
  console.log(erro.name, '|', erro.message);
}
propriedade que não existe: undefined typeof em nome inexistente: undefined ReferenceError | cupomGlobal is not defined TypeError | Cannot read properties of undefined (reading 'codigo')

Quatro fatos numa saída só. Pedir uma propriedade que não existe devolve undefined sem reclamar. Pedir uma variável que não existe lança ReferenceError — exceto dentro de typeof, o único operador que aceita nome inexistente sem explodir. E quando você encadeia mais um ponto depois de um undefined, o erro muda de família: vira TypeError, a mensagem mais comum de toda a linguagem, detalhada em TypeError: Cannot read properties of undefined.

Anatomia de uma stack trace

A stack trace é o histórico de quem chamou quem até o ponto da falha. Este exemplo tem três níveis de propósito:

js
function calcularFrete(pedido) {
  return pedido.entrega.cep;
}

function montarResumo(pedido) {
  return `frete para ${calcularFrete(pedido)}`;
}

console.log(montarResumo({ cliente: 'Ana', itens: 2 }));
file:///private/tmp/loja/e04.mjs:2 return pedido.entrega.cep; ^ TypeError: Cannot read properties of undefined (reading 'cep') at calcularFrete (file:///private/tmp/loja/e04.mjs:2:25) at montarResumo (file:///private/tmp/loja/e04.mjs:6:24) at file:///private/tmp/loja/e04.mjs:9:13 at ModuleJob.run (node:internal/modules/esm/module_job:439:25) at async node:internal/modules/esm/loader:633:26 at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) Node.js v24.16.0

Leia por partes:

  • Cabeçalho — arquivo, linha, o próprio código e o ^ na coluna exata. No DevTools esse trecho não aparece; ele é um extra do Node.
  • name: messageTypeError: Cannot read properties of undefined (reading 'cep'). O (reading 'cep') diz qual propriedade você tentou ler; o que estava undefined é o que vem antes dela, pedido.entrega.
  • Primeiro at — onde quebrou: calcularFrete, linha 2, coluna 25.
  • at seguintes — quem chamou, em ordem inversa. montarResumo chamou calcularFrete; a linha 9 do módulo chamou montarResumo.
  • Quadros de node:internal/ — encanamento do Node. No navegador, o equivalente são os quadros de biblioteca (react-dom, vue). Ignore.

O truque de leitura é este: desça a stack até o primeiro at que aponta para um arquivo seu e não é onde o erro estourou. No exemplo, é a linha 9 — e é justamente lá, no objeto sem entrega, que está a causa. O topo diz onde doeu; a descida diz onde nasceu.

Quando a stack fica longa demais, dá para cortá-la:

js
Error.stackTraceLimit = 3;

function nivel3() { throw new Error('estoque não encontrado'); }
function nivel2() { return nivel3(); }
function nivel1() { return nivel2(); }

try { nivel1(); } catch (erro) { console.log(erro.stack); }
Error: estoque não encontrado at nivel3 (file:///private/tmp/loja/e09.mjs:3:27) at nivel2 (file:///private/tmp/loja/e09.mjs:4:28) at nivel1 (file:///private/tmp/loja/e09.mjs:5:28)

O padrão do V8 é 10 quadros. Em log de produção com muita camada, subir esse número (Error.stackTraceLimit = 30) revela a origem que estava sendo cortada.

RangeError: quando o número ou a pilha estoura

O RangeError mais visto não vem de número nenhum — vem de recursão sem parada:

js
function totalComBrindes(valor) {
  return totalComBrindes(valor + 1);
}

console.log(totalComBrindes(0));
file:///private/tmp/loja/e05.mjs:2 return totalComBrindes(valor + 1); ^ RangeError: Maximum call stack size exceeded at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:3) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) at totalComBrindes (file:///private/tmp/loja/e05.mjs:2:10) Node.js v24.16.0

A assinatura é inconfundível: a mesma função repetida linha após linha. O Node imprimiu dez quadros porque esse é o padrão do V8 — a recursão real passou de dez mil chamadas antes de estourar. Quando isso aparece, procure o caso-base que falta — ou, em componente de interface, o efeito que atualiza o estado que dispara o mesmo efeito.

Erro silencioso: quando nada é lançado

O erro mais caro não é o que aparece em vermelho. É o que não aparece:

js
const itens = [
  { nome: 'Teclado mecânico', preco: '289,90' },
  { nome: 'Mouse sem fio', preco: '149.90' },
];

const total = itens.reduce((soma, item) => soma + parseFloat(item.preco), 0);

console.log('total  :', total);
console.log('formato:', total.toFixed(2));
console.log('quarto item:', itens[3]);
console.log('soma com string:', 10 + '5');
total : 438.9 formato: 438.90 quarto item: undefined soma com string: 105

Nenhum erro, e o total está errado. parseFloat('289,90') para na vírgula e devolve 289 — o preço perdeu noventa centavos e o cliente pagou a menos. Índice fora do array devolve undefined em vez de estourar, e 10 + '5' concatena em vez de somar.

Esses três comportamentos são a razão de o JavaScript ter fama de traiçoeiro: a linguagem prefere seguir adiante com um valor esquisito a interromper. É por isso que validar dado que vem de fora — API, formulário, localStorage — é trabalho seu, não do motor.

Por que a stack aponta para uma linha que não existe

Em produção o seu código passa por um bundler e vira uma linha só. A stack continua real, mas inútil:

js
function c(p){return p.entrega.cep}function m(p){return "frete para "+c(p)}console.log(m({cliente:"Ana"}));
/private/tmp/loja/bundle.min.js:1 function c(p){return p.entrega.cep}function m(p){return "frete para "+c(p)}console.log(m({cliente:"Ana"})); ^ TypeError: Cannot read properties of undefined (reading 'cep') at c (/private/tmp/loja/bundle.min.js:1:32) at m (/private/tmp/loja/bundle.min.js:1:71) at Object.<anonymous> (/private/tmp/loja/bundle.min.js:1:88) at Module._compile (node:internal/modules/cjs/loader:1854:14) at Object..js (node:internal/modules/cjs/loader:1985:10) at Module.load (node:internal/modules/cjs/loader:1577:32) at Module._load (node:internal/modules/cjs/loader:1379:12) at wrapModuleLoad (node:internal/modules/cjs/loader:255:19) at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) at node:internal/main/run_main_module:33:47 Node.js v24.16.0

at c (bundle.min.js:1:32) é tecnicamente correto e humanamente inútil: calcularFrete virou c, montarResumo virou m, e tudo mora na linha 1.

O source map é o arquivo que desfaz isso. Ele mapeia cada posição do bundle de volta para o arquivo, a linha e o nome originais. Com ele ativo, o DevTools mostra calcularFrete (pedido.js:2:25) no lugar de c (bundle.min.js:1:32).

Três coisas para conferir quando a stack sai minificada:

  • O build de desenvolvimento gera source map? Em Vite é o padrão; em produção, build.sourcemap: true.
  • O .map está sendo servido? A última linha do bundle deve ter o comentário //# sourceMappingURL=, e a requisição do .map não pode voltar 404.
  • O DevTools está com Settings → Preferences → Sources → Enable JavaScript source maps ligado.

Ler um erro em trinta segundos

Um roteiro de leitura, na ordem:

  1. Que família é? SyntaxError manda você para a sintaxe, e nada rodou. Qualquer outra família rodou até ali.
  2. A mensagem cita um nome? x is not defined, reading 'cep', x is not a function — esse nome é a pista. Procure onde ele deveria ter sido definido.
  3. Qual é o primeiro at de arquivo seu? É onde quebrou. O segundo diz quem chamou; costuma ser onde a causa mora.
  4. O valor era o esperado? Se a resposta é “não sei”, pare de adivinhar: coloque um breakpoint e inspecione o escopo no momento da falha.

Erro conhecido é erro barato. A trilha de JavaScript segue daqui para o depurador, onde essas mesmas stacks viram um lugar em que você pode parar o programa e olhar em volta. E se preferir revisar como capturar e relançar antes disso, volte em try catch em JavaScript — ou consulte o guia completo de JavaScript para ver onde cada assunto entra.

  • erros
  • stack trace
  • typeerror
  • referenceerror
  • console

Perguntas frequentes

Qual a diferença entre ReferenceError e TypeError?
ReferenceError é nome que não existe em lugar nenhum visível dali — quase sempre erro de digitação ou import esquecido. TypeError é nome que existe e vale alguma coisa, só que essa coisa não serve para o que você pediu: não é função, não tem propriedade, é null.
Por que o console.log antes do erro não apareceu?
Se o erro é um SyntaxError, nada do arquivo roda. O motor precisa entender o arquivo inteiro antes de executar a primeira linha, e um parêntese faltando na linha 40 impede a linha 1 de existir. Erro em tempo de execução é o contrário: tudo antes dele já rodou.
A stack trace se lê de cima para baixo ou de baixo para cima?
A primeira linha depois da mensagem é onde o erro aconteceu; as de baixo são quem chamou quem, na ordem inversa da execução. Comece pelo topo para saber onde quebrou e desça até achar a primeira linha de um arquivo seu — é ali que costuma estar a causa.
Meu erro aponta para bundle.js linha 1, e agora?
Você está lendo o arquivo minificado. Ative os source maps no build de desenvolvimento e o DevTools passa a mostrar o arquivo, a linha e os nomes de variável originais. Sem source map, uma stack de bundle serve só para confirmar que o erro existe.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MDN — Error — developer.mozilla.org
  2. MDN — Referência de erros do JavaScript — developer.mozilla.org
  3. Chrome DevTools — Map preprocessed code to source code — developer.chrome.com

Continue por aqui