Exception swallowing in finally
The gotcha
A return, break, or continue inside finally silently suppresses the active exception. The exception you thought would propagate is gone.
Minimal repro
def f():
try:
raise ValueError("boom")
finally:
return 42 # swallows the ValueError
print(f()) # 42 — no exception raised
Same with break/continue in a loop:
for i in range(3):
try:
raise ValueError("boom")
finally:
break # swallows the exception, exits the loop
print("survived")
Why it happens
Python’s spec: if finally clause executes a return/break/continue, that flow control supersedes any pending exception or in-progress return. The exception is just dropped.
This is rarely intentional. It’s almost always a bug — someone wrote finally: return last_value to “always return something” and accidentally swallowed errors.
How to avoid
- Don’t put
returninfinally. Set state in thetryand return after the block, or wrap differently. - If you genuinely want to convert exceptions to return values, do it explicitly with
except:
def f():
try:
return compute()
except ValueError:
return 42 # explicit fallback
# finally only does cleanup, no return
- Use
contextlib.suppressfor “ignore this error” patterns, which is at least visible:
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove("maybe.txt")
- Linters:
pylintflagsreturn-in-finally(W0150) andflake8-bugbearhas B012.
Bonus: exceptions raised in finally
If the body raises and finally raises a different exception, the original is “chained” via __context__ but the new one propagates. Python 3 keeps the original visible in the traceback.
try:
raise ValueError("first")
finally:
raise RuntimeError("second")
# traceback shows: "during handling of the above ValueError, the following exception occurred"
Interview angle
“What does this print and what does it raise?” with a try/raise/finally/return snippet. The unwary answer “it raises ValueError” is wrong — the return in finally swallowed it.