Exception chaining with raise from
Overview
Exception chaining links a new exception to the original exception that caused it, using the syntax `raise NewError() from original_error`. This explicitly sets the `__cause__` attribute on the new exception. try: int('not a number') except ValueError as e: raise RuntimeError('config parse failed') from e When this traceback prints, Python shows both errors: 'The above exception was the direct cause of the following exception' — the original `ValueError` stays visible for debugging, even though the caller only sees `RuntimeError`. If you want to intentionally hide the original error instead (e.g. hiding database internals from a user), use `raise NewError() from None` to suppress the chain entirely — no `__cause__` is set and no 'direct cause' line is printed.
When building professional applications, you don't want low-level errors (like `psycopg2.IntegrityError`) leaking into your high-level business logic. You catch the database error and raise a clean domain error like `DuplicateUserError`. However, if you don't chain them, you lose the original database traceback, making debugging a nightmare. `raise from` solves this by giving callers a clean domain error to catch, while preserving the full diagnostic history for the logs.
Where used: Service layers hiding repository/database implementation details, API clients translating HTTP errors into SDK-specific exceptions, Suppressing sensitive tracebacks using `from None`
Why learn this
- Preserving the root cause of an error while translating it to a domain-specific exception
- Suppressing noisy or sensitive tracebacks with `raise ... from None`
- Understanding implicit chaining (when Python automatically sets `__context__` because an error happened inside an `except` block)
Code walkthrough
def parse_config(data):
try:
return int(data['port'])
except (KeyError, ValueError) as e:
raise RuntimeError("Invalid config file") from e
try:
parse_config({})
except RuntimeError as e:
print(f"High-level error: {e}")
print(f"Low-level root cause: {type(e.__cause__).__name__}")
Focus: The caller catches `RuntimeError` cleanly, but the `__cause__` attribute retains the original `KeyError` for logging.
Common mistakes
- Losing the traceback: If you catch an error and just `raise MyCustomError()` without the `from` clause, Python implicitly links them, but it signals that the new error happened *during the handling* of the first one, not that it is a direct translation. Using `from e` is the semantic way to translate errors.
- Chaining the wrong object: You must chain from the exception instance, not the class. `raise MyError from ValueError` is invalid. You must catch the instance: `except ValueError as e: raise MyError from e`.
Glossary
- traceback
- A detailed report showing the sequence of function calls that led to an error in your code. Example: `Traceback (most recent call last):`
- suppress
- To intentionally prevent an error message or signal from being shown or passed further up the chain. Example: `raise MyError from None`
Recall questions
- What syntax is used to explicitly chain a new exception to an original exception?
- How do you suppress the original exception's traceback completely when raising a new exception?
Understanding checks
What special attribute is populated when using `raise NewError() from original_error`?
ValueError
The `from e` syntax explicitly links the original exception as the direct cause, storing it in the `__cause__` attribute of the new exception.
How do you completely hide the original exception's traceback when raising a new exception, preventing it from being printed?
By using the syntax `raise NewError() from None`.
Using `from None` explicitly suppresses exception chaining, hiding the original error completely from the logs.
Practice tasks
Translate a Database Error
Modify `create_user` to catch the `KeyError` thrown by `db_insert`. When caught, raise a `ValueError('User exists')` explicitly chained from the caught `KeyError`.
Challenge
Hide internal errors
Write `fetch_data()` that catches a `ConnectionError` and raises a `ServiceUnavailableError` from None to suppress the internal network stack trace.
Continue learning
Previous: try/except with specific exception types
Previous: Raising exceptions with raise
Previous: Custom exception classes