How to Print an Exception Chain in Python for Debugging
A helper that walks an exception's __cause__ and __context__ chain, printing each level with indentation to make debugging nested errors clearer.
Python code
36 linesimport sys
import traceback
def pretty_exception_chain(exc):
"""Print the full exception chain with cause/context details."""
chain = []
current = exc
seen = set()
while current is not None and id(current) not in seen:
seen.add(id(current))
chain.append(current)
current = current.__cause__ or current.__context__
for i, exception in enumerate(reversed(chain)):
indent = " " * i
print(f"{indent}Level {i}: {exception.__class__.__name__}: {exception}")
if exception.__cause__:
print(f"{indent} caused by:")
elif exception.__context__ and not exception.__suppress_context__:
print(f"{indent} context:")
def demonstrate_chain():
try:
try:
raise ValueError("invalid data")
except ValueError as e:
raise KeyError("missing key") from e
except KeyError as final_error:
print("--- Exception Chain ---")
pretty_exception_chain(final_error)
print("\n--- Standard traceback ---")
traceback.print_exc()
if __name__ == "__main__":
demonstrate_chain()
Output
--- Exception Chain ---
Level 0: KeyError: missing key
caused by:
Level 1: ValueError: invalid data
--- Standard traceback ---
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
raise ValueError("invalid data")
ValueError: invalid data
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 6, in <module>
raise KeyError("missing key") from e
KeyError: missing key
How it works
Exceptions maintain a chain through __cause__ (set explicitly with raise ... from) and __context__ (implicit when an exception is raised while handling another). This code walks that linked list using a set to prevent infinite loops in circular chains. Reversing the list shows the most recent exception first, with indentation reflecting depth. The __suppress_context__ flag tells whether the implicit context was overridden by an explicit cause, so the output labels the relationship correctly.
Common mistakes
- Using `__context__` alone, which misses explicitly set `__cause__` relationships.
- Forgetting to guard against circular exception chains, causing an infinite loop.
- Not checking `__suppress_context__` to distinguish explicit cause from implicit context.
- Confusing the order—printing the chain from oldest to newest without reversing.
Variations
- Use `traceback.format_exception(exc)` to get a formatted string instead of printing.
- Add a maximum depth parameter to avoid deep recursion in pathological chains.
Real-world use cases
- Diagnosing multi-layer service call failures where one error masks another.
- Building custom logging that captures the full root-cause story in incident reports.
- Teaching junior engineers to read implicit Python exception context in tests and CI logs.
Sponsored
More from Errors & debugging
- Catch RecursionError and Fail Gracefully in Python easy
- Catch ValueError and print friendly message in Python easy
- Collect Multiple Validation Errors in Python Before Raising medium
- Handle ValueError and ZeroDivisionError in Python with try except easy
- How to Add a Correlation ID to Logging Records in Python medium
- How to Assert Preconditions with Descriptive Messages in Python easy
Keep learning
Related tutorials and quizzes for this topic.