6 min read
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
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."
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?
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
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?
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 catchKeyboardInterruptin older versions — useexcept BaseException:only if you mean it. Modern guidance: catchException, notBaseException. finallyruns even if youreturninsidetry. 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
returninsidefinallyoverrides any return or exception fromtry. 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
tryblock. Wrap only the operation that can fail, not the surrounding code. Broadtryblocks 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 →
returninsidefinallyor a blanketexcept:. - trace points at the wrong place → the real failure was caught and rewrapped without
from/ chaining.
- Resource leak (file handle, lock) after an exception → missing
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?
4. Language Differences
| Aspect | Python | Java | C++ |
|---|---|---|---|
| Keyword | try / except / else / finally | try / catch / finally | try / catch (no finally) |
| Cleanup pattern | finally or with | finally or try-with-resources | RAII (destructors) |
| Catch-all | except: (not recommended) or except Exception: | catch (Exception e) | catch (...) |
| Can catch by type hierarchy | Yes | Yes | Yes |
else block (runs on success) | Yes | No | No |
| Rethrow | raise (bare) | throw; or throw e; | throw; (preserves type) |
| Exception chaining | raise X from e | Pass cause to constructor | std::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.
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?
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.
finallyvs. 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.finallyis 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.
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?
6. Interview Cheat Sheet
trycontains risky code;catch/excepthandles specific types;finallyruns no matter what (success, failure, orreturn).- Handlers match top-down by type — put specific exceptions before general ones.
finallyis 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
finallyrun if youreturninsidetry?" — Yes. It also runs on orbreak. 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.