Try Catch Finally

6 min read

Reading Progress0%
CS Fundamentals Index
Tier 1 -- Foundations
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs

Try Catch Finally

1. What Is It?

try / catch / finally is the syntax for handling exceptions. You put risky code in a try block, specify what to do on failure in catch (Python calls it except), and put cleanup code in finally — which runs whether the try succeeded, failed, or returned early.

Without this construct, a single failing operation somewhere deep in a call would either crash the whole program or silently corrupt state. With it, you can draw clear boundaries: "beyond this line I handle failure; inside the block I assume success."

QUICK CHECK

A backend service connects to a database and performs a query. The database connection must be closed after the operation, regardless of whether the query succeeds or throws an exception. Which block in a try/catch/finally construct is the right place to put the connection-closing logic?

Choose one answer

2. How It Works

The runtime models execution as three outcomes of the try block: normal completion, thrown, or early return/break. The catch/except clauses are matched against the 's type. finally always runs, after the try and any matching handler.

# Python
try:
    f = open("data.txt")
    data = f.read()
    process(data)
except FileNotFoundError as e:
    print("no file:", e)
except PermissionError as e:
    print("permission:", e)
except Exception as e:             # catch-all, rarely what you want
    print("something else:", e)
else:                              # runs only if no exception was raised
    print("success")
finally:
    f.close()                      # runs ALWAYS (if f was assigned)
// Java
try {
    String data = Files.readString(path);
    process(data);
} catch (NoSuchFileException e) {
    System.out.println("no file: " + e);
} catch (IOException e) {
    System.out.println("io: " + e);
} finally {
    // cleanup that must happen no matter what
}
// C++
try {
    std::ifstream f("data.txt");
    if (!f) throw std::runtime_error("cannot open");
    process(read_all(f));
} catch (const std::runtime_error& e) {
    std::cout << "runtime: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "std: " << e.what() << "\n";
}
// C++ has NO 'finally' — use RAII or scope guards

Handler matching

Handlers are tried top-down; the first matching type wins. This is why you put more specific types first: catch (NoSuchFileException) before catch (IOException). A generic handler at the top would swallow the more specific case.

Resource management: the real reason finally exists

Code often acquires resources (files, sockets, locks) that must be released whether things succeed or fail. finally guarantees that.

Python 2.5+ has a cleaner pattern: context managers (with).

with open("data.txt") as f:
    process(f.read())
# f.close() happens automatically, even on exception

Java 7+ has try-with-resources:

try (var f = Files.newBufferedReader(path)) {
    process(f.readLine());
}  // f.close() happens automatically

C++ doesn't have finally — it has RAII (Resource Acquisition Is Initialization). The destructor of a -allocated runs automatically when the exits, whether by normal return or by exception unwinding.

{
    std::ifstream f("data.txt");   // constructor acquires
    process(read_all(f));
}  // destructor releases — even if exception is thrown
QUICK CHECK

A backend service opens a database connection and runs a query inside a try block. You add two catch clauses: the first catches a specific DeadlockException, and the second catches a broader DatabaseException (which DeadlockException is a subclass of). In what order should these handlers be declared, and why?

Choose one answer

3. What You Actually Need to Know

  • Don't catch what you can't handle. catch ( e) {} — catching everything and doing nothing — is almost always a bug. At minimum, log it and rethrow; usually, let it propagate to a place that can actually respond.
  • Catch specific types, not base classes unless you genuinely want every subclass. except : in Python will also catch KeyboardInterrupt in older versions — use except BaseException: only if you mean it. Modern guidance: catch Exception, not BaseException.
  • finally runs even if you return inside try. This is load-bearing. It also runs if the exception is rethrown. It does NOT run if the process is killed (kill -9, sys.exit() via os._exit, segfault).
  • A return inside finally overrides any return or exception from try. This is almost always a bug — the original exception disappears silently.
  • Don't use exceptions for control flow. try: while True: x = next(it) except StopIteration: ... is the Python iterator idiom, but in most cases a conditional check is clearer and faster.
  • Narrow your try block. Wrap only the operation that can fail, not the surrounding code. Broad try blocks hide which line caused the failure.
  • Exception chaining preserves the original cause. Python: raise NewError("...") from original. Java: throw new MyException("...", original). Don't lose the original trace when rethrowing.
  • Debugging clues:
    • Resource leak (file handle, lock) after an exception → missing finally / context manager / RAII.
    • "Weird" behavior where errors vanish → return inside finally or a blanket except:.
    • trace points at the wrong place → the real failure was caught and rewrapped without from / chaining.
QUICK CHECK

A backend service processes file uploads. The upload handler has a finally block that closes the file handle and releases a lock. A developer adds a return success_response inside the finally block to ensure a response is always sent. What is the most significant risk of this pattern?

Choose one answer

4. Language Differences

AspectPythonJavaC++
Keywordtry / except / else / finallytry / catch / finallytry / catch (no finally)
Cleanup patternfinally or withfinally or try-with-resourcesRAII (destructors)
Catch-allexcept: (not recommended) or except Exception:catch (Exception e)catch (...)
Can catch by type hierarchyYesYesYes
else block (runs on success)YesNoNo
Rethrowraise (bare)throw; or throw e;throw; (preserves type)
Exception chainingraise X from ePass cause to constructorstd::throw_with_nested / manual

Python's else is under-used: it lets you say "if the try succeeds, do this" without widening the try block. Cleaner than adding more code inside try.

QUICK CHECK

A Python backend developer writes a function that opens a database connection, runs a query inside a try block, and wants to execute some result-processing logic only when the query succeeds — without expanding the try block to include that logic. Which language construct best fits this goal in Python?

Choose one answer

5. Tradeoffs & Decisions

  • Where to catch: close to the error vs. high in the call . Close-to-the-error catches let you retry or substitute a default. High-level catches (e.g., one per web request) turn arbitrary failures into a clean 500 response. Most real systems do both — local for recovery, global for last-resort logging.
  • finally vs. context managers / RAII. Context managers and RAII are declarative — the cleanup is tied to the resource's type, not copied at every call site. Prefer them. finally is for one-off ad-hoc cleanup that doesn't fit a resource pattern.
  • Rethrowing vs. wrapping. Rethrow when you can't add context. Wrap (with from e / chaining) when you want a domain-meaningful that still preserves the root cause.
  • Silent swallow vs. loud failure. Silent swallow is a debugging nightmare. If you genuinely must ignore an error (best effort), at least log it. Better: return a success/failure indicator to the caller.

If you see a broad try: ... except : pass, it usually hides a real bug that's happened for months. You'd choose a narrow catch of a specific type, do something meaningful with it (log, retry, return a default), and let everything else propagate.

QUICK CHECK

A backend service connects to a third-party payment API. A developer writes the following code: try: result = payment_api.charge(amount) except Exception: pass During a production incident, charges are silently failing for hours without anyone noticing. Which refactoring best addresses this problem?

Choose one answer

6. Interview Cheat Sheet

  • try contains risky code; catch/except handles specific types; finally runs no matter what (success, failure, or return).
  • Handlers match top-down by type — put specific exceptions before general ones.
  • finally is for cleanup that must run, typically releasing resources — but context managers (with) and try-with-resources / RAII are usually cleaner.
  • Never silently swallow exceptions — at minimum log them; usually let them propagate.
  • Don't use exceptions for control flow — they're slow and obscure intent.
  • C++ has no finally — it uses RAII, where destructors automatically release resources on exit.

Follow-ups:

  • "Does finally run if you return inside try?" — Yes. It also runs on or break. Not if the process is killed.
  • "Why is catch (Exception e) bad?" — It catches every error, including bugs and system signals you probably wanted to know about. Prefer specific types.
  • "Why doesn't C++ have finally?" — Because RAII makes it unnecessary. A -allocated 's destructor runs automatically when the exits, even during exception unwinding.
Glossary History

Click dotted jargon to save explanations here.

Glossary History

Click dotted jargon to save explanations here.