try / except — Handling Failure Gracefully
Errors aren’t the enemy — unhandled errors are. A program that crashes with a wall of red text loses the user; a program that says “That wasn’t a number — try again” keeps them. try/except is how you take control of failure.
The crash you already know
age = int(input("Age: ")) # user types "abc"
Output:
ValueError: invalid literal for int() with base 10: 'abc'
The whole program dies. In a real app, that’s a user lost. Here’s the same moment, handled:
The try/except structure
try:
age = int(input("Age: "))
print(f"Next year: {age + 1}")
except ValueError:
print("That's not a number!")
Python tries the indented block. If any line raises an error, it jumps immediately to the matching except block instead of crashing. The program continues afterward — failure handled, dignity intact.
Catching specific errors matters
# ❌ lazy — catches EVERYTHING, hides real bugs
try:
...
except:
print("Something went wrong")
# ✅ precise — catches exactly what you expect
try:
...
except ValueError:
print("Not a number")
Bare except swallows typos, logic bugs, everything — making bugs invisible instead of handled. Always name the exception. The ones you’ll meet most:
| Error | Raised when |
|---|---|
ValueError |
right type, bad value (int("abc")) |
TypeError |
wrong type entirely ("5" + 5) |
NameError |
undefined variable |
ZeroDivisionError |
dividing by zero |
KeyError |
missing dict key |
FileNotFoundError |
missing file |
Multiple excepts — different problems, different plans
try:
a = int(input("Numerator: "))
b = int(input("Denominator: "))
print(f"Result: {a / b}")
except ValueError:
print("Please enter numbers only")
except ZeroDivisionError:
print("Cannot divide by zero!")
Python checks except blocks in order and runs the first match. A non-number hits ValueError; a zero denominator hits ZeroDivisionError; success skips them all.
else and finally — the complete picture
try:
file_data = open("data.txt").read()
except FileNotFoundError:
print("File missing — starting fresh")
file_data = ""
else:
print("File loaded successfully") # runs only if NO error
finally:
print("(attempt finished)") # runs ALWAYS — error or not
- else — the “success path”: runs when the try block completed cleanly
- finally — the “always path”: cleanup that must happen no matter what (closing connections, files)
You’ll see else occasionally; finally becomes essential the day you work with databases and network connections.
The error object — reading the details
try:
age = int(input("Age: "))
except ValueError as err:
print(f"Could not convert: {err}")
as err captures the exception object — its text often names the exact offending value, which is gold for debugging.
The retry loop (putting it all together)
The pattern behind every “please enter a valid number” prompt ever written:
while True:
try:
age = int(input("Age: "))
break # success — leave the loop
except ValueError:
print("Numbers only, try again")
print(f"Got it: {age}")
Try → fail politely → loop → try again. Users forgive anything except being crashed on.
Common Errors & Fixes
- My except never fires — wrong exception type. Reproduce the crash, read its actual name, catch that one.
SyntaxErroronexcept ValueError as e:— check spelling and theaskeyword.- Code after the error doesn’t run — correct: execution jumps straight to except. Move dependent code into the try (after the risky line) or into else.
✅ Checkpoint
- What happens if no except matches the raised error? (The program crashes with the original traceback)
- Why avoid bare
except:? (Hides real bugs — always name the exception) - When does
elserun in try/except/else? (Only when the try block had no error) - Write the shape of a retry loop for int input. (while True: try/int/break, except ValueError/print)
Next: raising exceptions — making YOUR code fail loudly when its rules are broken.