Back to Python & Coding

PYTHON & CODING · QUESTION 84

How should you handle exceptions in Python?

On this page
Intermediate 4 min read
Interview-ready answerThe concise version to remember

Catch exceptions at a boundary where the program can recover, translate the error, or add useful context. Prefer specific exception types, keep the `try` block narrow, and preserve the original cause when wrapping with `raise ... from ...`. Do not silently swallow errors or use exceptions for ordinary control flow when a clear condition is available.

LET'S MAKE IT SIMPLE

First, the idea in plain English

An exception signals that an operation could not continue normally. A caller can handle it when it knows what recovery is appropriate, such as retrying a temporary network request or showing a useful validation message. Catching every error far from its source can hide bugs and make failures difficult to diagnose.

A USEFUL COMPARISON

Picture it this way

If a delivery is delayed, the person handling the shipment can reschedule it; they should not mark it delivered just to clear the queue. Error handling should either recover honestly, communicate failure, or add context while preserving the original problem.

FOLLOW THE JOURNEY

Handle failures at a boundary with a recovery policy

Show exception propagation, narrow handling, and explicit recovery or re-raising.

Handle failures at a boundary with a recovery policyShow exception propagation, narrow handling, and explicit recovery or re-raising.Error-handling flow
01Raise specific failureAn operation signals an exceptional condition with an appropriate type.
02Propagate through layersAllow errors to travel until a layer has a meaningful response.
03Recover or add contextHandle narrowly, or wrap the error while preserving its cause.
04Report honestlyLog or return a clear failure instead of pretending success.

This simplified visual highlights the main relationships. In practice, the exact design depends on the requirements; Silent fallbacks corrupt behavior and observability.

An operation raises a specific exception; it propagates to a boundary that either recovers, translates with the original cause, or re-raises it.

How to read this visual
This simplified visual highlights the main relationships. In practice, the exact design depends on the requirements; Silent fallbacks corrupt behavior and observability.

UNDER THE HOOD

How it works, step by step

01

Raise specific failure

An operation signals an exceptional condition with an appropriate type.

Why it matters: Specific types help callers decide what can be recovered.

02

Propagate through layers

Allow errors to travel until a layer has a meaningful response.

Why it matters: Premature handling can hide the actual failure.

03

Recover or add context

Handle narrowly, or wrap the error while preserving its cause.

Why it matters: A useful boundary can provide action without losing diagnosis.

04

Report honestly

Log or return a clear failure instead of pretending success.

Why it matters: Silent fallbacks corrupt behavior and observability.

OUTSIDE THE TEXTBOOK

A practical example

A service calls a payment provider. It catches a timeout to apply a bounded retry with an idempotency key, but it does not catch every exception and return “payment successful.” Unexpected programming errors propagate to the request boundary and are reported with a trace identifier.

GO A LITTLE DEEPER

Technical details that matter

  • `finally` runs during normal and exceptional exit and is suitable for cleanup.
  • Use `else` after `try` for work that should run only when no exception was raised.
  • Custom exceptions can represent domain failures and preserve lower-level causes with exception chaining.
Python example
try:
    amount = int(raw_amount)
except ValueError as error:
    raise ValueError("amount must be an integer") from error

The handler catches only conversion errors and adds domain-specific context. Exception chaining keeps the original cause available for debugging.

MAKE A GOOD CHOICE

When is this useful?

  • Recovering from expected input, I/O, or dependency failures.
  • Converting low-level errors into a clear domain-level contract.
  • Ensuring cleanup and observability at service boundaries.
Consider the alternative

Use return values or validation results for expected branches that are common and not exceptional. Reserve exceptions for failures that interrupt normal work.

WATCH OUT FOR

Common misunderstandings

  • Using a bare `except` and swallowing unrelated failures.
  • Catching exceptions without logging, recovery, or re-raising.
  • Wrapping errors without preserving their cause.

REAL-WORLD TRADE-OFFS

Limitations and failure cases

  • Broad handling can conceal defects.
  • Retries may repeat side effects unless operations are idempotent.
  • Exception messages can leak sensitive data if exposed directly to users.

WHAT TO REMEMBER

Key takeaways

  1. 1Catch specific errors where recovery is possible.
  2. 2Keep handlers narrow and preserve causes.
  3. 3Do not return success-shaped results after an unhandled failure.

PRACTICE THE FOLLOW-UP

What might an interviewer ask next?

When should you retry an exception?

Retry transient failures with bounded attempts, backoff, and idempotent effects. Validation or permission errors generally need correction, not retry.

What is exception chaining?

Using `raise NewError(...) from original` records the original exception as the cause while presenting a higher-level error.

SAY IT OUT LOUD

How to explain this in an interview

“I catch specific exceptions at a layer that can recover or provide useful context. I keep `try` blocks narrow, use bounded retries only for transient failures with safe side effects, and preserve causes when wrapping errors. I avoid silent catches and success-shaped fallbacks because they hide defects and mislead callers.”

Continue your learning path

Connect this question to the roadmap topic it supports.

Open roadmap topic