Aikido

Por que você deve tratar erros em blocos catch em vez de deixá-los vazios

Legibilidade

Regra
Manusear erros em catch blocos. 
Vazio catch vazios silenciosamente engolir erros, 
tornando a depuração difícil. 
Linguagens suportadas: Java, C, C++, PHP, JavaScript, 
TypeScript, Go, Python

Introdução

Blocos `catch` vazios são um dos anti-padrões mais perigosos em código de produção. Quando exceções são capturadas, mas não tratadas, o erro desaparece sem deixar rastros. A aplicação continua executando com estado corrompido, dados inválidos ou operações falhas que deveriam ter interrompido a execução. Usuários veem falhas silenciosas onde funcionalidades não operam, mas não recebem mensagens de erro. Equipes de operações não têm logs para depurar. A única indicação de que algo está errado surge horas ou dias depois, quando falhas em cascata tornam o sistema inutilizável.

Por que isso importa

Depuração e resposta a incidentes: Blocos `catch` vazios eliminam logs de erro. Engenheiros não têm rastreamento de pilha, mensagem de erro ou indicação de quando ou onde a falha ocorreu, tornando os problemas quase impossíveis de reproduzir.

Corrupção silenciosa de dados: Quando operações de banco de dados ou chamadas de API falham dentro de blocos `catch` vazios, a aplicação continua como se tivessem sido bem-sucedidas. Registros são parcialmente atualizados, transações ficam incompletas e, quando a corrupção é descoberta, o rastro de auditoria já desapareceu.

Vulnerabilidades de segurança: Blocos catch vazios mascaram falhas de segurança como erros de autenticação ou verificações de autorização. Um atacante que aciona uma exceção em um caminho crítico de segurança pode ignorar as proteções completamente se o erro for silenciosamente suprimido.

Falhas em cascata: Quando os erros são suprimidos, a aplicação continua em um estado inválido. Operações subsequentes que dependem do resultado da operação falha também falharão, criando uma cadeia de falhas que desvia os engenheiros da causa raiz real.

Exemplos de código

❌ Não-conforme:

async function updateUserProfile(userId, profileData) {
    try {
        await db.users.update(userId, profileData);
        await cache.invalidate(`user:${userId}`);
        await searchIndex.update(userId, profileData);
    } catch (error) {
        // TODO: handle error
    }

    return { success: true };
}

Por que está errado: Se qualquer operação falhar, o erro é silenciosamente ignorado e a função retorna sucesso. O banco de dados pode ser atualizado, mas a invalidação do cache pode falhar, deixando dados desatualizados. Ou a atualização do índice de busca falha, tornando o usuário inlocalizável, sem nenhum log ou alerta para indicar o problema.

✅ Compatível:

async function updateUserProfile(userId, profileData) {
    try {
        await db.users.update(userId, profileData);
        await cache.invalidate(`user:${userId}`);
        await searchIndex.update(userId, profileData);
        return { success: true };
    } catch (error) {
        logger.error('Failed to update user profile', {
            userId,
            error: error.message,
            stack: error.stack
        });
        throw new ProfileUpdateError(
            'Unable to update profile',
            { cause: error }
        );
    }
}

Por que isso importa: Cada erro é registrado com contexto, fornecendo informações para depuração. O erro se propaga para o chamador, permitindo o tratamento adequado de erros no nível apropriado. Sistemas de monitoramento podem alertar sobre esses erros, e a aplicação falha rapidamente em vez de continuar com um estado inválido.

Conclusão

Blocos `catch` vazios nunca são aceitáveis em código de produção. Toda exceção capturada precisa, no mínimo, ser logada, e a maioria precisa ser propagada para os chamadores ou acionar ações de recuperação específicas. Se você realmente precisar ignorar um erro, documente o motivo com um comentário explicando a justificativa de negócio. O padrão deve ser sempre tratar os erros explicitamente, e não descartá-los silenciosamente.

FAQs

Dúvidas?

E se eu realmente precisar ignorar certos erros?

Documente-o explicitamente com um comentário explicando por que o erro pode ser ignorado com segurança. Registre o erro no nível de debug para que ele apareça em logs detalhados, mas não acione alertas. Considere se ignorar o erro pode levar a um estado inválido. Mesmo para erros 'esperados', como falhas de cache ou tempos limite de rede, o registro ajuda as equipes de operações a entender os padrões de comportamento do sistema.

Devo sempre registrar erros em blocos catch?

O logging geralmente é uma boa ideia porque você não consegue depurar problemas sem ver o que falhou. Há casos em que você pode rastrear o problema sem logs, como relançar imediatamente o erro para ser tratado em outro lugar, ou se a aplicação deve falhar e reiniciar para falhas críticas. Mas um logging adequado sempre ajuda.

Qual é a diferença entre logging e relançar erros?

O logging registra o que aconteceu para depuração e monitoramento. Relançar propaga o erro para os chamadores para que eles possam decidir como responder. Faça ambos: registre o erro com contexto no ponto da falha e, em seguida, relance (possivelmente encapsulado em um tipo de erro mais específico) para permitir que os chamadores lidem com a recuperação. Não registre o mesmo erro em vários níveis, isso cria ruído.

Como lidar com erros que ocorrem em blocos finally?

Blocos finally raramente devem lançar erros. Se eles precisarem realizar operações propensas a erros (como fechar recursos), envolva-as em seus próprios try-catch. Registre quaisquer erros, mas não permita que eles mascarem o erro original. Algumas linguagens fornecem sintaxe para lidar tanto com o erro principal quanto com erros de blocos finally; use esses mecanismos para preservar ambos os contextos de erro.

E quanto ao impacto de performance do logging de cada erro?

O logging é barato comparado ao custo de depurar problemas de produção sem logs. Frameworks de logging modernos são altamente otimizados. Se você tem tantos erros que o logging afeta o desempenho, corrija os erros em vez de escondê-los. Altas taxas de erro indicam problemas sérios que blocos catch vazios apenas piorarão.

Blocos catch devem sempre lançar erros ou podem retornar valores de erro?

Depende da linguagem e da arquitetura. Em JavaScript com promises, lançar uma exceção de um bloco catch propaga para o próximo handler de erro. Retornar um objeto de erro de um bloco catch resolve a promise com esse erro, o que geralmente está incorreto. Familiarize-se com a semântica de tratamento de erros da sua linguagem. Geralmente, deixe os erros propagarem, a menos que você possa se recuperar de forma significativa.

Como lidar com erros em operações assíncronas que não possuem try-catch?

Use handlers .catch() em promises, listeners de evento de erro em event emitters, ou callbacks de erro em APIs baseadas em callback. Nunca ignore handlers de rejeição ou callbacks de erro. Rejeições de promise não tratadas devem ser monitoradas no nível do processo e tratadas como falhas críticas. O Node.js moderno pode encerrar em rejeições não tratadas, o que é melhor do que uma falha silenciosa.

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.