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
f = None
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:
if f is not None: # finally runs ALWAYS; open() may have failed
f.close()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 exceptionJava 7+ has try-with-resources:
try (var f = Files.newBufferedReader(path)) {
process(f.readLine());
} // f.close() happens automaticallyC++ 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 thrownMatch the exception, then clean up
A PermissionError from process skips the first handler, reaches the matching handler, and still closes the file in finally.
The try block groups work that may fail.
Text version
- The try block groups work that may fail.
- The file opens and reads data so process can use its contents.
- Processing raises PermissionError.
- Python checks handlers from top to bottom, skips FileNotFoundError because the type differs, and matches PermissionError.
- Only the first matching handler runs and prints the error.
- The error skips else, but finally still closes the file.
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 everyand 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 Exception:in Python will also catchKeyboardInterruptin older versions — useexcept BaseException:only if you mean it. Modern guidance: catch, 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,os._exit(), segfault).sys.exit()raisesSystemExit, sofinallystill runs.- 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
Cleanup happens before an early return
A return inside try waits while finally runs. If cleanup finishes normally, the original return proceeds.
Returning inside try still requires cleanup before the function exits.
Text version
- Returning inside try still requires cleanup before the function exits.
- The runtime runs finally before it returns to the caller.
- If cleanup finishes normally, the original return proceeds, but a return inside finally would replace it.
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 (Throwable t) (catch (Exception e) misses Error) | catch (...) |
| Can catch by type hierarchy | Yes | Yes | Yes |
else block (runs on success) | Yes | No | No |
| Rethrow | raise (bare) | 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.
Cleanup patterns across three languages
Python and Java offer finally and resource constructs, while C++ ties cleanup to destructors through RAII.
Python and Java have finally, while C plus plus uses other cleanup mechanisms.
Text version
- Python and Java have finally, while C plus plus uses other cleanup mechanisms.
- Python uses the with statement, Java uses try with resources, and C plus plus releases resources in destructors.
- Python runs else only when try finishes normally, without an exception or an early exit.
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 everyException, including bugs likeNullPointerExceptionyou 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.