Guia completo de tratamento de erros em JavaScript
Erros em JavaScript podem derrubar uma aplicação se não forem tratados. Este guia mostra como usar try/catch, finally e lançar exceções personalizadas com throw, transformando falhas em respostas previsíveis.

Erro em JavaScript não é fracasso: é informação. Quando um TypeError ou ReferenceError interrompe a execução, o problema não é o erro em si, mas a ausência de uma resposta previsível. Um sistema que ignora falhas silencia dados corrompidos, experiência quebrada e horas de depuração cega. Tratar erros não é opcional, é a diferença entre código que apenas funciona e código que resiste.
Neste guia, percorremos as quatro camadas do tratamento de erros em JavaScript: captura com try/catch, execução garantida com finally, lançamento controlado com throw e criação de erros semânticos com Error customizado. Ao final, você terá um padrão replicável para qualquer aplicação.
Passo 1: Capturar exceções com try/catch
O bloco try envolve o código que pode lançar uma exceção. Se algo falhar, o controle passa imediatamente para o catch, que recebe o objeto de erro como argumento. A estrutura básica é:
try { JSON.parse('dado inválido'); } catch (erro) { console.error('Falha ao parsear:', erro.message); }
Dica: nunca deixe o catch vazio. Um bloco catch sem corpo engole o erro e dificulta a depuração. Se não for possível tratar, ao menos registre com console.error ou envie para um serviço de monitoramento.
Erro comum: capturar Error genérico e tratar todos os erros da mesma forma. Um TypeError (ex.: propriedade de undefined) exige ação diferente de um SyntaxError (ex.: JSON malformado). No Passo 4, veremos como criar erros específicos para cada cenário.
Passo 2: Garantir execução com finally
O bloco finally executa sempre, após o try bem-sucedido ou após o catch. É o lugar ideal para liberar recursos, fechar conexões ou resetar estados, independentemente de ter ocorrido erro ou não.
function buscarDados(url) { const conexao = abrirConexao(); try { return fetch(url); } catch (erro) { console.error('Falha na requisição:', erro); return null; } finally { conexao.fechar(); // sempre executado } }
Dica: finally também executa se houver um return no try ou catch. A ordem é: executa o bloco, avalia o finally e só então retorna o valor.
Erro comum: colocar lógica de limpeza fora do finally, achando que o código após try/catch será executado. Se uma exceção não for capturada, o fluxo é interrompido e a limpeza nunca ocorre.
Passo 3: Lançar erros com throw
Use throw para interromper o fluxo quando uma condição inválida for detectada. Você pode lançar qualquer valor (string, número, objeto), mas a prática recomendada é lançar uma instância de Error para manter a pilha de chamadas (stack trace).
function dividir(a, b) { if (b === 0) { throw new Error('Divisão por zero não permitida'); } return a / b; }
Dica: lance cedo, trate tarde. Valide entradas no início da função e lance o erro imediatamente. Deixe o tratamento para quem chamou a função, que tem contexto para decidir como reagir.
Erro comum: lançar strings soltas (throw 'erro'). Isso perde o stack trace e dificulta a identificação da origem do erro em produção. Sempre use new Error('mensagem').
Passo 4: Criar erros customizados
Para diferenciar tipos de erro, estenda a classe Error e adicione propriedades semânticas. Isso permite que o catch filtre por tipo de erro em vez de depender de mensagens de texto.
class ErroValidacao extends Error { constructor(campo, mensagem) { super(mensagem); this.name = 'ErroValidacao'; this.campo = campo; } }
try { throw new ErroValidacao('email', 'Formato inválido'); } catch (erro) { if (erro instanceof ErroValidacao) { console.log(Campo ${erro.campo}: ${erro.message}); } }
Dica: defina uma hierarquia de erros para sua aplicação, ErroNegocio, ErroTecnico, ErroValidacao, e trate cada um em camadas diferentes (UI, serviço, API).
Erro comum: criar uma classe de erro sem chamar super(mensagem). Sem isso, a propriedade message não é definida e o stack trace fica incompleto.
Checklist rápido
- [ ] Todo bloco
trytem umcatchcom ação útil (log, fallback ou relançamento). - [ ]
finallyusado para limpeza de recursos (conexões, timers, arquivos). - [ ] Erros lançados com
new Error()ou subclasse, nunca com tipos primitivos. - [ ] Erros customizados estendem
Errore chamamsuper(). - [ ] O
catchdiferencia o tipo de erro antes de tratá-lo.
FAQ
Qual a diferença entre throw e return?
throw interrompe a execução do bloco atual e propaga a exceção para o catch mais próximo na pilha de chamadas. return encerra a função e devolve um valor ao chamador, sem interromper o fluxo geral.
Devo tratar todos os erros com try/catch?
Não. Use try/catch apenas para operações que podem falhar de forma imprevisível: parsing, requisições de rede, acesso a arquivos, conversões de tipo. Validações de entrada podem ser feitas com condicionais antes da execução.
O que acontece se eu não tratar um erro?
Se uma exceção não for capturada por nenhum catch, ela se propaga até o topo da pilha e o ambiente (navegador ou Node.js) exibe um erro não tratado, muitas vezes interrompendo a execução do script.
Como capturar erros em código assíncrono?
Em Promises, use .catch() ou try/catch com async/await. Em callbacks, o padrão é passar o erro como primeiro argumento (error-first callback).
Posso usar finally sem catch?
Sim. try/finally sem catch é válido: o bloco finally executa mesmo que o erro não seja capturado. Use quando você quer garantir limpeza, mas não deseja tratar o erro naquele ponto.
Como depurar erros customizados?
Acesse erro.stack para ver a pilha de chamadas. Ferramentas como Chrome DevTools e VS Code mostram o stack trace no console. Em produção, serviços como Sentry ou LogRocket capturam e agrupam erros por tipo.