Exceptions and try/except

Handle problems without crashing

Beginner 11 min

In this lesson

An exception is an error Python raises while your program runs — like a missing file, or letters typed where you wanted a number. Exception handling with try/except lets your program catch these and respond gracefully instead of crashing.

Explain it like I’m 5

A try/except is a safety net. You ‘try’ something risky, and if it goes wrong, the ‘except’ catches the fall and decides what to do instead of letting the program crash.

try and except

Put risky code in a try block. If it raises an exception, Python jumps to the matching except block instead of crashing: try: number = int(text) / except ValueError: print("Not a number"). If nothing goes wrong, the except is skipped. This lets you plan for failure at a specific spot.

Example
def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "Cannot divide by zero"

print(safe_divide(10, 2))
print(safe_divide(10, 0))
Output
5.0
Cannot divide by zero
Catching a specific exception and recovering.

Wrap the conversion in try/except so that invalid input prints 'Please enter a number' instead of crashing. Test it with the given non-numeric value.

text = "abc"
number = int(text)
print("You entered", number)

Catch specific exceptions, not everything

Name the exception you expect: except ValueError:, except FileNotFoundError:. A bare except: catches everything, including bugs and typos you’d rather see — it hides problems. Catch the narrowest exception that makes sense, so genuine surprises still surface.

Example
values = ["10", "x", "30"]
total = 0
for v in values:
    try:
        total += int(v)
    except ValueError:
        print("Skipping:", v)
print("Total:", total)
Output
Skipping: x
Total: 40
Skipping bad data without stopping the loop.

else and finally

else runs only if the try succeeded with no exception. finally runs no matter what — success, failure, even if you return — which makes it the place for cleanup like closing a file. Together they let you separate ‘the risky part’, ‘what to do on success’, and ‘what to do always’.

Example
try:
    n = int("10")
except ValueError:
    print("Not a number")
else:
    print("Parsed successfully:", n)
finally:
    print("Done trying.")
Output
Parsed successfully: 10
Done trying.
else runs on success; finally runs every time.

Raising your own, and when to let it crash

You can signal a problem with raise ValueError("price must be positive"). But don’t catch exceptions you can’t meaningfully handle — if there’s nothing sensible to do, letting the program crash with a clear traceback is better than swallowing the error and continuing in a broken state.

Example
def set_price(amount):
    if amount < 0:
        raise ValueError("price must be positive")
    return amount

print(set_price(10))
print(set_price(-5))
Output
10
ValueError: price must be positive
Raise an exception to reject bad input.

Catching several types, and inspecting the error

One except can handle several exception types at once — list them in parentheses: except (ValueError, TypeError):. You can also capture the exception object with as e, which lets you read its message, log it, or check exactly which error occurred with type(e).__name__.

This keeps related failures in one tidy handler while still letting you react to the specifics.

Example
for value in ["42", "oops", None]:
    try:
        print("Parsed:", int(value))
    except (ValueError, TypeError) as e:
        print("Skipped", repr(value), "-", type(e).__name__)
Output
Parsed: 42
Skipped 'oops' - ValueError
Skipped None - TypeError
One handler, two error types, with the specifics available.

Common mistake: Catching and silently ignoring exceptions

Why it happens:

Writing except: pass makes errors disappear, which feels like ‘fixing’ them but actually hides bugs and bad data.

How to fix it:

Catch the specific exception and do something meaningful — log it, use a default, or inform the user. Only ignore an exception when that’s genuinely the right behaviour.

Common mistake: Wrapping too much code in one try

Why it happens:

A giant try block makes it unclear which line might fail and can catch errors you didn’t intend to.

How to fix it:

Keep try blocks small — ideally around the single risky operation — so the handling is precise.

What does a try/except block let you do?

Why prefer except ValueError: over a bare except:?

Mini exercise (medium)

Write to_int(text) that returns the integer value of text, or 0 if it can’t be converted. Test it with "15" and "oops".

Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.

def to_int(text):
    # TODO: return int(text), or 0 if it cannot be converted
    return 0

print(to_int("15"), to_int("oops"))

What to learn next

You learned what exceptions are, how try/except catches them, how to target specific ones, the roles of else and finally, and when to handle a problem versus letting it crash. You wrote a to_int() that returns int(text) or falls back to 0 when the text isn’t a number.

Your programs still forget everything when they close. Saving to disk is the fix. Reading and Writing Files covers opening files, the all-important with statement, read and write modes, and looping over lines. When you’re ready, continue to the next lesson.