Python raise from Exception Chaining with cause
Python's raise ... from ... syntax enables explicit
exception chaining by directly linking an original error to a newly
raised error via the __cause__ attribute. This mechanism
preserves the underlying root cause of a failure while allowing code to
translate low-level or library-specific errors into high-level,
domain-specific exceptions. This guide explains how explicit chaining
works under the hood, how it alters the __cause__ attribute
and traceback reporting, and how to use it to suppress unwanted
context.
Implicit vs. Explicit Chaining
When an unhandled exception occurs inside an except
block without using from, Python automatically performs
implicit exception chaining:
try:
int("invalid")
except ValueError as err:
raise RuntimeError("Conversion failed")In this case, Python sets the __context__ attribute of
the RuntimeError to the ValueError. The
standard traceback displays:
During handling of the above exception, another exception occurred:
Implicit chaining is designed to capture unexpected bugs that happen while attempting to handle an existing error.
Explicit Exception
Chaining with raise ... from
Explicit chaining is intentional. When you deliberately catch an
exception and want to wrap it into a higher-level exception, you use the
raise ... from ... syntax:
try:
int("invalid")
except ValueError as err:
raise RuntimeError("Application error occurred") from errWhen Python executes raise ExcB from ExcA:
- Assigns
__cause__: The__cause__attribute ofExcBis set directly toExcA. - Sets
__suppress_context__: Python setsExcB.__suppress_context__ = True. This signals to the interpreter that the relation between the two exceptions is intentional, so the default__context__formatting is bypassed. - Modifies the Traceback: The default traceback
output explicitly states the causal relationship:
The above exception was the direct cause of the following exception:
Inspecting
__cause__ Programmatically
Because the original exception is stored on the new exception object, it can be accessed programmatically in subsequent error handlers:
try:
try:
open("nonexistent_file.txt")
except FileNotFoundError as original:
raise CustomAppError("Resource missing") from original
except CustomAppError as final_error:
print(f"Caught: {final_error}")
print(f"Direct cause: {final_error.__cause__}")
print(f"Cause type: {type(final_error.__cause__).__name__}")This ensures that downstream error monitoring tools, loggers, or recovery routines can inspect the original failure without parsing string-based tracebacks.
Suppressing
Exception Context with from None
The raise ... from ... syntax also provides a way to
deliberately hide an underlying exception using
from None:
try:
int("not_a_number")
except ValueError:
raise KeyError("Key could not be resolved") from NoneExecuting raise ... from None:
- Sets
__cause__toNone. - Sets
__suppress_context__toTrue.
This strips the original ValueError entirely from the
traceback. Users and loggers only see the KeyError, which
is useful for preventing internal implementation details and noisy
tracebacks from leaking into public APIs.