Skip to content

raise and Custom Exceptions

diagram raise from, bare raise, and raise from None mermaid
Raising inside an except block always records what you were handling, so the original error is never lost by accident. The three forms differ in what the traceback claims about the relationship: a deliberate cause, an incidental one, or none shown at all. Choosing wrongly either hides a root cause or blames the wrong thing.

You can use raise to:

  • validate function inputs
  • enforce rules
  • stop invalid workflows early
raise_example.py
def set_age(age: int) -> int:
    if age < 0:
        raise ValueError("age must be >= 0")
    return age
 
print(set_age(10))
# print(set_age(-1))
raise_message.py
score = -5
if score < 0:
    raise ValueError(f"score must be non-negative, got {score}")

Custom exceptions help you represent domain-specific errors.

custom_exception.py
class PaymentError(Exception):
    """Raised when a payment fails."""
 
 
def charge(amount: float) -> None:
    if amount <= 0:
        raise PaymentError("Amount must be positive")
 
 
try:
    charge(0)
except PaymentError as e:
    print("Payment failed:", e)

Sometimes you want to log something and re-raise.

reraise.py
try:
    int("abc")
except ValueError as e:
    print("Logging error:", e)
    raise
  • Don’t leak secrets in error messages.
  • Use clear messages: what went wrong + what to do.
  • Prefer built-in exception types when they fit.
sketch Three ways to raise inside an except block p5.js
Raising while handling always records what you were handling, so the original is never lost by accident. The three forms differ in what the traceback claims about the relationship -- a deliberate cause, an incidental one, or none displayed. The last row is the one people get wrong: from None hides the original, it does not erase it.
pch.quizTag pch.quizDefaultTitle
  1. Inside an `except`, you write `raise ValueError(...) from e`. What is set?

    pch.quizShowAnswer

    C — Both `__cause__` and `__context__` — Verified. `from e` sets `__cause__` explicitly, and `__context__` is set automatically because you were handling something at the time.

  2. What does `raise ... from None` actually do?

    pch.quizShowAnswer

    B — Suppresses its DISPLAY; `__context__` is still set — Verified: `__cause__` is None but `__context__` still holds the original. Anything walking the chain programmatically can still reach it.

  3. A traceback says 'During handling of the above exception, another exception occurred'. What does that indicate?

    pch.quizShowAnswer

    B — Implicit chaining — a bare `raise` inside an `except` — That wording comes from `__context__`. 'The above exception was the direct cause' is the other one, produced by `raise ... from`.

  4. When should you wrap a library exception in your own type with `from e`?

    pch.quizShowAnswer

    B — When the original explains the new one and callers should not depend on the library's type — Wrapping gives callers a stable exception type to catch, and `from e` keeps the root cause in the traceback so debugging is not made harder.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading