What you'll learn
- How
try,except,elseandfinallyfit together - How to catch specific exceptions and handle several types
- How to use the exception object to report what went wrong
- How to re-raise an exception and when to do it
- The common mistakes that make error handling worse, not better
Why use try/except?
Exceptions are normal in real programs:
- network failures
- missing files
- invalid user input
Instead of crashing, you can handle the problem and continue.
Basic try/except
try:
x = int(input("Enter a number: "))
print(10 / x)
except Exception:
print("Something went wrong")This works, but it’s usually too broad.
Catch specific exceptions
try:
x = int(input("Enter a number: "))
print(10 / x)
except ValueError:
print("Please enter only digits.")
except ZeroDivisionError:
print("Number can’t be 0.")Catch multiple exception types
try:
x = int(input("Enter a number: "))
print(10 / x)
except (ValueError, ZeroDivisionError) as e:
print("Error:", e)Capture the exception object
try:
int("abc")
except ValueError as e:
print("Type:", type(e))
print("Message:", e)How Python picks an except block
When an exception is raised inside try, Python stops running the body right there. It then checks each except clause from top to bottom and runs the first one that matches. If none match, the exception keeps travelling up to the caller, and eventually ends the program with a traceback.
Because the first match wins, order matters. Put specific exceptions before general ones:
try:
items = [1, 2, 3]
print(items[10])
except LookupError:
print("caught by the general parent")
except IndexError:
print("never reached")IndexError is a child of LookupError, so the first clause always wins and the second is dead code. Swap them and the specific message runs.
The else block
else runs only when the try body finished without an exception. Use it to keep the try body small, so you only protect the line that can actually fail.
def read_number(text):
try:
number = int(text)
except ValueError:
print("Not a number:", text)
return None
else:
print("Parsed OK")
return number
print(read_number("42"))
print(read_number("forty-two"))>>> read_number("42")
Parsed OK
42
>>> read_number("forty-two")
Not a number: forty-twoThe finally block
finally runs every time: after success, after a handled error, even if the function returns early or an unhandled exception is flying past. It is the place for cleanup.
def load(path):
f = None
try:
f = open(path, encoding="utf-8")
return f.read()
except FileNotFoundError:
print("File not found:", path)
return ""
finally:
if f is not None:
f.close()
print("File closed")
print(load("missing.txt"))For files, a with statement does the same cleanup in fewer lines. Reach for finally when you manage something that has no context manager.
Putting all four together
def divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("Cannot divide by zero")
result = None
except TypeError as err:
print("Bad operand types:", err)
result = None
else:
print("Division succeeded")
finally:
print("divide() finished")
return result
divide(10, 2)
divide(10, 0)
divide(10, "x")Re-raising an exception
Sometimes you want to do something (log, clean up) but still let the error continue. A bare raise inside an except block re-raises the current exception with its original traceback.
def parse_age(text):
try:
return int(text)
except ValueError:
print("Logging: invalid age received:", text)
raise
try:
parse_age("abc")
except ValueError as err:
print("Caller handled:", err)You can also translate one exception into another and keep the original as the cause with raise ... from ...:
class ConfigError(Exception):
pass
def load_port(value):
try:
return int(value)
except ValueError as err:
raise ConfigError("PORT must be a number") from err
try:
load_port("eighty")
except ConfigError as err:
print(err, "| caused by:", repr(err.__cause__))Common mistakes
| Mistake | Why it hurts | Better |
|---|---|---|
Bare except: | Also catches KeyboardInterrupt and SystemExit, so Ctrl+C stops working | Catch a named exception |
except Exception: pass | Hides bugs completely | Handle it, log it or re-raise it |
Huge try bodies | You cannot tell which line failed | Wrap only the risky line |
| Wrong clause order | A general parent hides a specific child | Specific first, general last |
| Using exceptions for normal flow | Slower and harder to read | Use an if when a check is simple |
Losing the traceback with raise err in a new place | Confusing debugging | Use bare raise |
# Bad: the typo is silently ignored
try:
total = pricee * 2
except Exception:
pass
# Better: only catch what you expect
prices = {"apple": 3}
try:
total = prices["pear"] * 2
except KeyError:
total = 0
print(total)Best practices
- Catch the narrowest exception you can.
- Don’t silently ignore exceptions.
- Log exceptions in production systems.
- Avoid
except Exception:unless you re-raise or report clearly. - Keep the
trybody short and move the success path intoelse. - Use
finallyorwithfor cleanup.
Visualize it
try / except / else / finally is a small decision tree. Python runs the try body; if an
exception is raised it jumps to a matching except; the else runs only when no
exception occurred; and finally always runs, error or not:
flowchart TD
A([Enter try]) --> B["Run try body"]
B --> C{"Exception raised?"}
C -- yes --> D["Run matching except"]
C -- no --> E["Run else block"]
D --> F["Run finally (always)"]
E --> F
F --> G([Continue])
Check what you learned
When does the else block of a try statement run?
else is the success path. It runs after the try body completes with no exception, and before finally.
Show answer
C — Only when the try body finished without raising an exception — else is the success path. It runs after the try body completes with no exception, and before finally.
Why is a bare except: clause considered a mistake?
A bare except catches everything, including exceptions meant to stop the program. Name the exceptions you expect.
Show answer
B — It also catches KeyboardInterrupt and SystemExit, and hides bugs by treating every problem the same — A bare except catches everything, including exceptions meant to stop the program. Name the exceptions you expect.
Inside an except block you want to log the problem and let it continue to the caller. What do you write?
A bare raise re-raises the active exception and keeps its original traceback.
Show answer
C — raise (with no arguments) — A bare raise re-raises the active exception and keeps its original traceback.
🧪 Try It Yourself
Exercise 1 – Catch a ValueError
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 2 – Multiple Except Blocks
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.Exercise 3 – Raise a Custom Exception
Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.
Press Run to see what your code prints.