AI Skill Report Card

Handling Exception Context

A-84·Aug 13, 2026·Source: Web
14 / 15
Python
# Implicit chaining (__context__): happens automatically when an exception # is raised inside an except/finally/with block. try: 1 / 0 except ZeroDivisionError: raise RuntimeError("computation failed") # Traceback shows both exceptions, joined by: # "During handling of the above exception, another exception occurred." # Explicit chaining (__cause__): use `raise ... from ...` to state the cause # deliberately, e.g. when translating a low-level error into a domain error. try: connect() except ConnectionError as e: raise ServiceUnavailable("cannot reach backend") from e # Traceback says: "The above exception was the direct cause of the # following exception." # Suppress chaining entirely with `from None`. try: parse(data) except ValueError: raise ValueError("bad input") from None
Recommendation
Add an example showing exception group (except*) interaction with __context__ since Python 3.11+ changes traceback nesting behavior
13 / 15

Progress checklist for reasoning about or writing chained-exception code:

  • Identify whether the new exception is raised while handling another (implicit context) or is caused by another (explicit cause you want documented)
  • Decide if the original exception is genuinely relevant to the caller — if not, suppress it
  • Choose the right mechanism: implicit (__context__), explicit (raise X from Y, sets __cause__ and implies __suppress_context__ = True), or suppressed (raise X from None)
  • Verify traceback output matches intent (correct linking message, or no extra context)
  • For libraries: prefer explicit from e when translating exceptions across abstraction boundaries so users can inspect the root cause via e.__cause__
Recommendation
Include a quick reference table mapping mechanism -> attribute set -> traceback message for faster scanning
  • __context__: Set automatically whenever an exception is raised inside the handling of another exception (except, except*, finally, or with block). Represents "this happened while dealing with that."
  • __cause__: Set only via raise NewExc from original_exc. Represents an explicit, intentional causal link. Setting __cause__ also sets __suppress_context__ = True so the implicit context isn't printed redundantly.
  • __suppress_context__: When True, the traceback printer skips showing __context__. Set implicitly by raise ... from ..., or explicitly by raise ... from None.
  • Traceback messages:
    • Implicit: "During handling of the above exception, another exception occurred:"
    • Explicit cause: "The above exception was the direct cause of the following exception:"
  • from None: Fully suppresses display of the previous exception (sets __cause__ = None and __suppress_context__ = True).
  • All three attributes are inspectable on exception instances at runtime, not just in tracebacks (err.__cause__, err.__context__, err.__suppress_context__).
16 / 20

Example 1 — Implicit context (accidental double failure): Input:

Python
try: open("missing.txt") except FileNotFoundError: log.write("error") # log is undefined -> NameError

Output: Traceback shows FileNotFoundError first, then "During handling of the above exception, another exception occurred:", then NameError. Both are genuinely useful for debugging, so leave as-is.

Example 2 — Explicit cause (intentional translation): Input:

Python
try: json.loads(payload) except json.JSONDecodeError as e: raise ConfigError("invalid config file") from e

Output: Traceback shows the original JSONDecodeError, then "The above exception was the direct cause of the following exception:", then ConfigError. config_error.__cause__ is the JSONDecodeError instance.

Example 3 — Suppressed context (irrelevant internal detail): Input:

Python
try: cache[key] except KeyError: raise LookupError(f"{key} not found") from None

Output: Traceback shows only LookupError; the internal KeyError is hidden because it's an implementation detail irrelevant to the caller.

Recommendation
Show a 'bad outcome' example where suppressing context accidentally hides a real bug, to reinforce the pitfall more concretely
  • Use from e in library/API boundary code so callers can programmatically inspect err.__cause__ for root-causing.
  • Use from None when the intermediate exception is purely an implementation detail (e.g., dict lookups, internal parsing) that would confuse end users.
  • Don't silently swallow context you might need later — prefer suppressing display (from None) over losing information (e.g., logging the original before raising).
  • When re-raising the same exception type for retry logic, plain raise (no argument) preserves the original traceback without adding a new context frame.
  • Don't assume except Exception: raise NewError() loses the original — it's still attached via __context__ and will print unless suppressed.
  • Don't use from None reflexively; it destroys debugging information for legitimate infrastructure/logic errors.
  • Don't confuse __cause__ (explicit, from raise...from) with __context__ (implicit, automatic) — only one drives the "direct cause" message, the other drives "during handling."
  • Remember raise X from Y sets __suppress_context__ = True automatically — the implicit context still exists on the object but won't print in the default traceback.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
13/15
Examples
16/20
Completeness
17/20
Format
14/15
Conciseness
14/15