Skip to main content
Python Basics2026-07-29 · 9 min read

Python Error Handling: try / except / else / finally in 2026

Error handling separates "code that works on your machine" from "code that survives production". Python's try / except looks simple, but the four-keyword full form — try / except / else / finally — encodes a surprising amount of structure most tutorials skip.

This guide covers: what each keyword actually means, the six patterns you'll see in every backend codebase, when to define your own exceptions, and the three anti-patterns that let bugs slip past your tests.

The four keywords, precisely

▶ PYTHONeditable · runs in your browser

The else clause is under-used. It says "this next bit depends on the try succeeding, but I don't want it protected by the same except". It's the right place for the happy path continuation — putting it inside try accidentally catches unrelated exceptions from ship(result).

The six patterns

1. Retry on transient failure

▶ PYTHONeditable · runs in your browser

Rule: retry ONLY on transient errors (network, timeout, 429, 503). Never retry on ValueError, KeyError, TypeError — those are code bugs, and retrying just runs the bug three times.

2. Guard with else

▶ PYTHONeditable · runs in your browser

The else guarantees file was opened successfully AND scopes cleanup with with. Notice: process() might raise ValueError — that will propagate up, NOT be caught by except FileNotFoundError.

3. Cleanup with finally (before with existed)

▶ PYTHONeditable · runs in your browser

In modern code you'd use with db.connect() as conn:with is the sugar over try / finally for the common cleanup case. Reach for explicit finally only when the resource doesn't support the context-manager protocol.

4. Re-raise with added context

▶ PYTHONeditable · runs in your browser

raise NewException(...) from e chains the original exception onto the new one so the traceback shows BOTH. Never write raise NewException(str(e)) — that discards the original traceback.

5. Catch the narrowest exception you can

▶ PYTHONeditable · runs in your browser

Rule: the narrower the except, the better. If you don't know what process can raise, run it and find out. except Exception: is a code smell 90% of the time.

6. raise inside except — re-raise cleanly

▶ PYTHONeditable · runs in your browser

Bare raise keeps the original traceback intact. Never raise e (Python 3 is smart about it, but bare raise is the idiomatic form).

Custom exceptions — when it's worth it

Define your own exception class when:

1. Callers need to except MyError: specifically (i.e., you have a callable API and users need to branch on failure type).

2. Your library / service raises multiple related errors that share context.

▶ PYTHONeditable · runs in your browser

Now callers can catch broadly (except PaymentError) or narrowly (except CardDeclinedError). Structured attributes on the exception (e.card_last4) let error handlers build useful UX without parsing exception messages.

Anti-pattern: defining a custom exception that only exists to be raised once and never caught. That's just raise ValueError("..."). Custom exceptions earn their weight from being caught.

Three anti-patterns that hide bugs

Anti-pattern 1: Bare except

▶ PYTHONeditable · runs in your browser

Bare except catches signals that should exit the program (Ctrl-C, sys.exit, MemoryError). Never write this. If you truly want to catch every non-fatal exception, use except Exception: — which STILL catches too much, but at least respects Ctrl-C.

Anti-pattern 2: Silencing without logging

▶ PYTHONeditable · runs in your browser

Every caught exception should either be handled (with a specific fallback) or logged. pass in an except is a bug in slow motion — the code appears to work while silently discarding data.

▶ PYTHONeditable · runs in your browser

Anti-pattern 3: except Exception: around everything

▶ PYTHONeditable · runs in your browser

Problem: if stripe.charge succeeded but send_receipt failed, the customer got charged AND you swallowed the error. The except covers four different failure modes, and you can't tell them apart.

Fix: narrow the try scope + branch the except by module boundary.

▶ PYTHONeditable · runs in your browser

Each try covers ONE operation. Each except knows what to do about THIS specific failure. That's production-grade error handling.


Good error handling is more design than syntax. try / except / else / finally gives you the tools; the discipline is picking the right except type, the narrowest possible try scope, and never silencing errors you don't understand.

Next step: the Foundations track walks through exception handling, custom exceptions, and the with statement — with real Python running in your browser.

Ready to try it?

15 lessons free after a free signup — no card. Sample the platform, then decide.

Start free

Or unlock every lesson

Pro — $12/mo, unlimited AI, cancel any time. Refund within 14 days worldwide.

See Pro plans

Get one Python lesson + one career idea every Friday

No spam, no "buy our course now". Three bullets, every Friday. Unsubscribe with one click.