Syllabus Lesson 41 of 239 · Files, Errors & Modules
Files, Errors & Modules

Handling Errors with try/except

Things go wrong: a file is missing, a number will not parse, you divide by zero. When Python hits an error it raises an exception, and unless you handle it the program stops. The try/except block lets you catch the problem and keep going.

try:
    value = int("not a number")
except ValueError:
    value = 0
print(value)  # 0

Catch the specific exception type, not a bare except:. Common ones: ValueError (bad value to a function), ZeroDivisionError, KeyError (missing dict key), FileNotFoundError, TypeError. You can capture the error object to inspect it:

try:
    risky()
except ValueError as e:
    print(f"bad value: {e}")

There are two optional clauses. else runs only if no exception was raised. finally runs no matter what, perfect for cleanup that must always happen:

try:
    n = int("5")
except ValueError:
    print("failed")
else:
    print("parsed ok")
finally:
    print("done")

Re-raising and chaining

Catching an exception does not mean you have to silence it. If you only want to react to a failure (log it, clean up) but still let it stop the program, use a bare raise inside the except block to re-raise the same exception, traceback and all:

try:
    save(data)
except OSError:
    print("save failed, not hiding it")
    raise   # re-raise the same error so callers still see it

When you want to turn a low-level error into a clearer one for your callers, raise the new exception from the original. This chains them, so the traceback shows your error and the underlying cause instead of losing it:

try:
    config = json.loads(text)
except ValueError as e:
    raise RuntimeError("config file is not valid JSON") from e

Both keep the failure visible. Silently swallowing an error with except: pass hides bugs and is one of the most common mistakes in real code.

A handy pattern is a function that returns a safe fallback instead of crashing.

Your turn

Write a function safe_divide(a, b) that returns a / b, but if b is zero it catches the ZeroDivisionError and returns the string "undefined" instead. So safe_divide(10, 2) returns 5.0 and safe_divide(10, 0) returns "undefined".

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output