For most of my first year, my programs had two states: working and dead. A traceback is friendlier than it looks once you read it from the bottom up, but a program that dies because someone typed cold where a number belonged isn't a program you can hand to another human. try/except is how a crash turns into a message.
Here's the crash in miniature:
raw = "cold"
minutes = int(raw)
ValueError: invalid literal for int() with base 10: 'cold'
The temptation is to wrap the entire program in a try so nothing can ever crash. Resist it. A try block is a claim that the lines inside might legitimately fail — so wrap only the line that can, and name the exception you expect:
try:
minutes = int(raw)
except ValueError:
print(f"{raw!r} isn't a number - using 30")
minutes = 30
A bare except: — no name — catches everything, including the TypeError from a typo three lines up and the KeyboardInterrupt from Ctrl+C, which I once learned the hard way when a script simply refused to die. Naming the exception keeps the failure honest: you handle what you expected, and genuine bugs still stop the program and get read.
Two optional blocks complete the shape:
try:
with open("last_run.txt", encoding="utf-8") as f:
stamp = f.read().strip()
except FileNotFoundError:
stamp = "never"
else:
print("last run:", stamp) # only if the try succeeded
finally:
print("check complete") # either way
First run, there's no file, so you get just check complete; every run after, the stamp prints first. else runs when nothing failed — keeping the success path out of the try keeps the try small. finally runs no matter what happened, which makes it the place for "always do this". In practice: try/except almost always, else now and then, finally about once a year.
Put together, here's the pattern I use for any input that might be wrong. The loop doesn't die on bad input; it argues back:
while True:
raw = input("minutes to back up (a number): ")
try:
minutes = int(raw)
break
except ValueError:
print(f"{raw!r} isn't a number - try again")
print(f"starting the {minutes}-minute backup")
Type ten and it asks again; type 30 and it moves on. The except names ValueError because the last line of the crash told me so — when you're unsure which exception to catch, trigger the failure once on purpose and read the name. That's the connection between the two skills: tracebacks tell you what failed, and try/except is what you do about the failures you expected.
One boundary I try to respect: try/except is for expected failures — bad input, missing files, a flaky network — not for hiding bugs. If an exception surprises you, let it crash and read the traceback. Errors are information, and a program that never crashes isn't finished; it's deaf.