Módulo 12 - Exceções, logging e robustez

raise from: encadear erros sem perder a causa

9 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Traduzir um erro técnico em um erro claro do seu domínio.
  • Usar raise ... from ... para preservar a causa original.
  • Entender __cause__ e __context__ no rastreamento.
  • Saber quando usar from None para ocultar a causa de propósito.

Traduzir erros de baixo nível

Uma prática comum e saudável é traduzir erros. Uma camada baixa do seu código pode falhar com um erro técnico, como um KeyError ao ler um dicionário ou um ValueError ao converter um texto. Repassar esse erro cru para quem chamou a sua função vaza detalhes de implementação e confunde. O melhor é capturar o erro técnico e levantar uma exceção do seu domínio, com uma mensagem clara. O cuidado é não jogar fora a causa original, que é ouro na hora de depurar.

class ConfigInvalida(Exception):
    pass

def ler_porta(config):
    try:
        return int(config["porta"])
    except KeyError as e:
        raise ConfigInvalida("faltou a chave porta") from e
    except ValueError as e:
        raise ConfigInvalida("porta precisa ser um numero") from e

try:
    ler_porta({"host": "localhost"})
except ConfigInvalida as erro:
    print(erro)              # faltou a chave porta
    print(repr(erro.__cause__))  # KeyError('porta')

raise ConfigInvalida(...) from e preserva o KeyError original em __cause__.

O from e é o que faz a mágica. A função devolve um erro limpo do domínio, ConfigInvalida, mas guarda o KeyError original em __cause__. No rastreamento completo, o Python imprime as duas exceções, com a frase A causa direta da exceção acima foi entre elas. Quem lê o log entende tanto o que deu errado do ponto de vista do domínio quanto a causa técnica exata, sem precisar adivinhar. É a diferença entre um log que ajuda e um que esconde.

__cause__ e __context__

O Python encadeia exceções de duas formas. Quando você usa raise ... from ..., ele preenche __cause__: uma ligação explícita, feita por você, dizendo que um erro foi a causa direta do outro. Mas há também o encadeamento implícito. Se uma exceção acontece dentro de um bloco except, enquanto você já tratava outra, o Python preenche __context__ automaticamente, mesmo sem from. No rastreamento, isso aparece como Durante o tratamento da exceção acima, ocorreu outra exceção.

SituaçãoAtributo preenchidoMensagem no rastreamento
Você usa raise ... from causa__cause__A causa direta da exceção acima foi
Erro novo dentro de um except, sem from__context__Durante o tratamento da exceção acima, ocorreu outra
Você usa raise ... from Nonenenhum (causa oculta)Só a nova exceção aparece

As três formas de encadeamento e como cada uma aparece no log.

A distinção importa na prática. O __context__ implícito é útil, mas às vezes polui o rastreamento com um erro intermediário que não interessa a quem lê. Quando você usa from explicitamente, deixa claro qual é a causa que importa. E, quando quer suprimir a cadeia de propósito, existe o from None, que oculta a causa. Um caso típico é uma função pública que não quer expor detalhes internos: ela traduz o erro e usa from None para mostrar só a mensagem limpa, sem o rastro técnico.

Quando ocultar a causa

class UsuarioNaoEncontrado(Exception):
    pass

_banco = {"ana": 1, "bruno": 2}

def id_do_usuario(nome):
    try:
        return _banco[nome]
    except KeyError:
        # Aqui a causa tecnica (KeyError) nao agrega nada a quem chama.
        raise UsuarioNaoEncontrado(f"usuario {nome!r} nao existe") from None

try:
    id_do_usuario("carla")
except UsuarioNaoEncontrado as e:
    print(e)              # usuario 'carla' nao existe
    print(e.__cause__)    # None: a causa foi ocultada de proposito

from None oculta o KeyError interno, expondo só a mensagem clara do domínio.

O bom senso aqui é claro: preservar a causa deve ser o padrão, e ocultá-la, a exceção. Um KeyError que revela a estrutura interna de um dicionário privado talvez não interesse a quem consome uma API pública, e nesse caso from None limpa o rastreamento sem perda real. Mas nunca use from None para esconder um erro que você não entendeu, porque é exatamente a causa que você precisa ver depois. Encadear bem é um dos hábitos que mais economizam tempo de depuração num sistema em produção.

Teste rápido

O que raise NovoErro(...) from erro_original faz?

Perguntas frequentes

Se eu não usar from, perco a causa original?
Não necessariamente. Se o novo erro é levantado dentro de um except, o Python liga as exceções por __context__ automaticamente, e o rastreamento ainda mostra as duas. O from serve para tornar essa ligação explícita e marcar qual é a causa direta que importa.
Qual a diferença prática entre __cause__ e __context__?
__cause__ é preenchido por você, com from, e sinaliza uma causa direta e intencional. __context__ é preenchido pelo Python quando um erro ocorre durante o tratamento de outro, sem from. Ambos aparecem no rastreamento, com frases diferentes que indicam a natureza da ligação.
Quando devo usar from None?
Quando a causa técnica não agrega valor a quem vai ver o erro e só poluiria o log, como esconder um detalhe interno numa API pública. Nunca use from None para ocultar um erro inesperado, porque é justamente essa causa que você vai precisar investigar depois.
Traduzir erros não é esconder informação?
Não, quando feito com from. Traduzir dá a quem chama uma mensagem clara do domínio, enquanto o from preserva a causa técnica no rastreamento. Você ganha clareza sem perder rastreabilidade. O que esconde informação é engolir o erro sem encadear nem registrar.
raise from funciona com exceções embutidas?
Sim. Você pode encadear a partir de qualquer exceção, embutida ou própria, e para qualquer exceção. O padrão comum é capturar uma embutida de baixo nível, como KeyError ou ValueError, e levantar uma exceção do seu domínio a partir dela com from.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.