🐍 Python Course · Stage 4 · Lesson 69 of 89 · Concept: Error Handling
Course Overview →

🟩 These concept pages connect Python to programming in general. Read Beginner tab first.

← Concept: Inheritance Concept: Modules →
thecodex.expert  ·  The Codex Family of Knowledge
CS Concepts

Error Handling

Programs fail at runtime — files not found, networks timing out, invalid input. Every language needs a strategy for what happens when things go wrong.

CS Concepts Language-independent ~18 min read Last verified:
Canonical Definition

Error handling is the set of mechanisms a programming language provides for detecting, signalling, and responding to conditions that prevent normal program execution — including exceptions and try/catch/finally blocks, explicit error return values, Result and Option types, and checked vs. unchecked error models.

🟩 Beginner

When things go wrong — how programs handle it

What you'll learn: What exceptions are and why they exist. try/except/finally as a universal pattern. The golden rule: fail loudly, recover intentionally.

How to read this tab: Compare with Python's Error Handling page which covers Python-specific syntax. This page explains the concept across all languages.

⏱ 20 min 📄 4 sections 🔶 Prerequisite: Functions
One sentence that captures it

Error handling is the plan your program has for when something unexpected happens — instead of crashing silently, it catches the problem and decides what to do.

An exception is a signal that something went wrong — a structured way to stop normal execution and report the problem. The alternative is returning error codes (C's approach) — but error codes are easy to ignore silently. Exceptions cannot be ignored: if uncaught, they crash the program with a clear message. "Loud failure" is safer than "silent wrong result."

What is an exception?

An exception is a signal that something went wrong. Opening a missing file, dividing by zero, accessing an index out of bounds — these raise exceptions. Without handling, the program crashes. With handling, you intercept the exception and decide: recover, log, or re-raise.

💡

The shape of exception handling is universal. try (risky code), catch/except (handle the error), finally (always runs). This appears in Python, Java, JavaScript, Go (defer), and Rust (Drop trait). The concept is identical; only the keywords differ. finally is particularly important: it guarantees cleanup even when an exception occurs.

Try / except / finally

Pythonerror_handling.py
try:
    with open("data.csv") as f:
        content = f.read()
except FileNotFoundError:
    print("File not found — using default")
    content = ""
except PermissionError as e:
    print(f"Permission denied: {e}")
    raise          # re-raise: let it propagate
finally:
    print("Always runs — cleanup goes here")
📎

Go and Rust treat errors as return values, not special control flow. Go functions return (result, error). Rust functions return Result. Callers must explicitly handle both cases — the compiler won't let you ignore errors. This makes error handling visible in the code. Python's exceptions are implicit — you can always forget to catch them.

Result types: error as a value

Rust uses Result<T, E> — either Ok(value) or Err(error). The compiler requires you to handle both cases. Go conventionally returns (value, error) from fallible functions.

Rustresult.rs
use std::fs;

fn read_file(path: &str) -> Result<String, std::io::Error> {
    fs::read_to_string(path)
}

match read_file("data.csv") {
    Ok(content) => println!("{}", content),
    Err(e)      => println!("Error: {}", e),
}

// ? operator: propagate error up the call stack
fn process() -> Result<(), std::io::Error> {
    let content = read_file("data.csv")?;
    println!("{}", content);
    Ok(())
}

Two rules for professional error handling: 1) Raise exceptions as soon as you detect bad input — don't let wrong data propagate deeper into the program. 2) Catch exceptions at the level that can actually do something about them — not everywhere just to silence them. The worst pattern: except: pass. It hides every error, including the bugs you need to find.

The golden rule: fail loudly, recover intentionally

Never silently swallow errors (except: pass is almost always wrong). Catch only what you can actually handle. Let unexpected errors propagate. A crash with a clear error message is better than a program continuing with corrupt state.

✅ Beginner tab complete — check your understanding

  • I can explain what an exception is and why "crashing with a clear error" is better than silent failure
  • I know the golden rule: raise exceptions early (when bad input arrives) and catch them late (at the level that can actually handle them)
  • I know the difference between catching a specific exception vs catching all exceptions

Continue to Modules →

🔵 Intermediate

Checked vs unchecked, Result types, and Option types

What you'll learn: Checked exceptions (Java) vs unchecked exceptions (Python, JavaScript). Result types — treating errors as values (Rust, Go). Option/Maybe types for nullable values.

How to read this tab: Understanding these different error models makes you appreciate the trade-offs each language makes. Go's error-as-value and Rust's Result are alternatives to exception systems.

⏱ 20 min 📄 2 sections 🔶 Prerequisite: Beginner tab
📎

Java distinguishes checked (must be handled) from unchecked (can ignore). Python only has unchecked exceptions — you can always forget to catch them. Java's checked exceptions force you to handle or re-declare every checked exception, making error paths explicit in method signatures. Python's approach is more flexible; Java's is more explicit about error contracts.

Checked vs. unchecked exceptions

Java distinguishes: checked exceptions (must be declared with throws or caught — compile error otherwise; e.g. IOException) and unchecked exceptions (RuntimeException subclasses — no declaration needed; e.g. NullPointerException). Checked exceptions proved controversial in practice — they propagate through call chains, forcing many functions to declare exceptions they can't handle. Most modern languages (Python, Rust, Go, Kotlin, C#) avoid checked exceptions.

💡

Python uses None where other languages use null. The difference: TypeScript's string | null forces you to check for null before using it (null safety). Python's Optional[str] hint says "this can be None" but doesn't enforce checking at runtime. Kotlin, Swift, and Rust all have language-level null safety; Python relies on discipline and type checkers.

Option types and null safety

Rust's Option<T> is either Some(value) or None. Kotlin uses nullable types (String?). Swift uses Optional<T>. These make the possibility of absence explicit in the type system — you cannot call methods on a nullable value without first unwrapping it.

Error handling across languages

LanguageMechanismForce handling?
PythonExceptions (try/except)No — unchecked
JavaChecked + unchecked exceptionsYes for checked
RustResult and Option typesYes — compiler enforces
GoMultiple return (value, error)Convention only
JavaScriptExceptions + Promise .catch()No
CError codes (int return values)No — easily ignored
Commonly confused
A bare except: pass is not error handling — it is error hiding. Catching without logging, recovering, or re-raising means the program continues with unknown state. Catch only what you know how to handle.
Exceptions are not for flow control. Using try/except KeyError to test dict membership instead of if key in d or d.get(key) is an antipattern — exceptions have overhead and obscure intent.
Go's error values are not the same as Rust's Result type. Go conventionally returns (value, err) but nothing prevents ignoring err. Rust's Result is enforced by the type system — the compiler won't let you use a Result as a value without handling both variants.
How this connects
Requires first
Confused with

✅ Intermediate tab complete — check your understanding

  • I can explain the difference between checked exceptions (compiler-enforced) and unchecked (Python's approach)
  • I understand Go's error-as-value pattern: functions return (result, error) and callers check the error
  • I know what Option/Maybe types are in Rust/Haskell: representing "value or nothing" without null

Continue to Modules →

🔴 Expert

Zero-cost exceptions and algebraic error types

What you'll learn: Zero-cost exception implementations — how modern languages implement exceptions with no overhead unless raised. Algebraic data types for error handling (Rust's Result). Formal error propagation semantics.

How to read this tab: Read when studying language implementation or when working in Rust.

⏱ 20 min 📄 2 sections 🔶 Prerequisite: After Stage 2

Zero-cost exceptions

In C++ and Rust, exceptions/panics are implemented with zero-cost abstractions: when no exception is thrown, the happy path incurs zero runtime overhead — no condition checks, no status register writes. Exception-handling tables are stored in a separate binary section. When an exception occurs, the runtime walks these tables to find the handler. Cost is paid only on the exceptional path. This contrasts with C-style error codes: checking every return value on the happy path costs cycles even when nothing goes wrong.

Algebraic data types for error handling

Rust's Result<T, E> is a sum type (discriminated union) — either Ok(T) or Err(E). The compiler requires exhaustive matching: a match that doesn't handle both variants is a compile error. This is the strongest static guarantee of forced error handling in any mainstream language — stronger than Java's checked exceptions because it cannot be suppressed with a throws Exception declaration.

Specification reference

Python: Language Reference §8 — Compound statements. Rust: The Rust Book ch09 — doc.rust-lang.org/book. Java: Java Language Specification §11 — Exceptions. Go: go.dev/blog/error-handling-and-go.

✅ Expert tab complete

  • I know Python's exceptions have zero overhead on the non-exception path
  • I understand Rust's Result type and the ? operator for propagation

Continue to Modules →

Sources

1
Python Software Foundation. Python Language Reference, §8.4. docs.python.org/3/reference/compound_stmts.html.
2
The Rust Book, Chapter 9 — Error Handling. doc.rust-lang.org/book/ch09-00-error-handling.html.
3
Java Language Specification §11 — Exceptions. Oracle. docs.oracle.com/javase/specs/.
4
Go Blog. Error handling and Go. go.dev/blog/error-handling-and-go.
Source confidence: High Last verified: Primary source: Python Language Reference §8.4 — The try statement