What you'll learn
Quick Answer
Python's try/except lets your program catch errors instead of crashing. You put risky code in a try block, and Python runs the matching except block if an error happens. Add else for code that should run only when there is no error, finally for cleanup that always runs, and use raise to signal your own errors. Always catch specific exception types like ValueError rather than a bare except.
What are exceptions in Python?
Every Python program hits errors sometimes. Maybe a user types text where you expected a number, a file is missing, or you divide by zero. In Python, these runtime problems are called exceptions.
An exception is different from a syntax error. A syntax error means Python cannot even understand your code, so nothing runs at all. An exception happens while your program is running: the code is valid, but something went wrong along the way.
When an exception is raised and nothing handles it, Python stops the program and prints a traceback — the red error message showing what went wrong and where. Here is a simple one:
print(10 / 0)
# ZeroDivisionError: division by zeroThat ZeroDivisionError is the exception's type. Python has many built-in types like ValueError, TypeError, KeyError, and FileNotFoundError. Learning to handle them is what error handling is all about.
Python try except: the basic structure
The core tool for handling exceptions is the try/except block. You put the risky code inside try, and the recovery code inside except. If the try block raises an exception, Python jumps straight to the matching except block instead of crashing.
Here is a script that crashes when the user types something that is not a number:
number = int(input("Enter a number: "))
print("You typed:", number)
# If the user types "hello":
# ValueError: invalid literal for int() with base 10: 'hello'Now wrap it in try/except so the program stays in control:
try:
number = int(input("Enter a number: "))
print("You typed:", number)
except ValueError:
print("That was not a whole number. Please try again.")If the user types hello, Python raises ValueError inside the try block, skips the rest of it, and runs the except block. The program prints a friendly message and finishes normally instead of showing a scary traceback.
Catching specific exception types
You should tell Python which exceptions you expect. A single try block can have several except blocks, each for a different type:
try:
number = int(input("Enter a number: "))
result = 100 / number
print("100 divided by your number is", result)
except ValueError:
print("Please enter a whole number.")
except ZeroDivisionError:
print("You cannot divide by zero.")Python checks the except blocks top to bottom and runs the first one that matches. If you want to handle several types the same way, group them in a tuple:
try:
risky_operation()
except (ValueError, TypeError):
print("Something was wrong with the input.")To read details about the error, capture it with as:
try:
number = int("abc")
except ValueError as e:
print("Could not convert:", e)
# Could not convert: invalid literal for int() with base 10: 'abc'The variable e holds the exception object, and printing it shows Python's own explanation of what went wrong.
Using else and finally
The full structure has two more optional parts: else and finally.
- else runs only if the try block finished with no exception.
- finally runs no matter what — whether there was an error or not. It is perfect for cleanup like closing a file or a database connection.
try:
number = int(input("Enter a number: "))
except ValueError:
print("That was not a number.")
else:
print("Thanks! Your number squared is", number ** 2)
finally:
print("This line always runs.")Why put the success code in else instead of at the end of try? Because it keeps the try block small. The try block should only contain the one risky line you actually want to guard, so you do not accidentally catch an exception thrown by unrelated code.
Raising your own errors with raise
Sometimes you want to signal that something is wrong. The raise keyword lets you throw an exception on purpose. This is useful for validating input inside your own functions.
def set_age(age):
if age < 0:
raise ValueError("Age cannot be negative.")
if age > 150:
raise ValueError("That age is not realistic.")
return age
print(set_age(25)) # 25
print(set_age(-4)) # ValueError: Age cannot be negative.By raising a clear exception, you stop bad data early and give the caller a precise message. You can catch it just like any built-in exception:
try:
set_age(-4)
except ValueError as e:
print("Invalid age:", e)For bigger projects you can even define your own exception types by inheriting from Exception:
class OutOfStockError(Exception):
pass
raise OutOfStockError("This item is sold out.")
A crashing script vs a graceful one
Let's put it together with a common task: asking the user for their age. First, the fragile version that crashes on bad input:
# Crashes if the user types text; also accepts silly values like -5
age = int(input("Enter your age: "))
print("Next year you will be", age + 1)Now the graceful version. It validates the input, raises its own error for impossible ages, and loops until the value is good:
while True:
try:
age = int(input("Enter your age: "))
if age < 0 or age > 150:
raise ValueError("Age must be between 0 and 150.")
except ValueError as e:
print("Invalid input:", e)
else:
print("Next year you will be", age + 1)
breakHere is the difference at a glance:
| Behaviour | Crashing script | Graceful script |
|---|---|---|
| Handles text like "hello" | No | Yes |
| Rejects impossible ages | No | Yes |
| Shows a friendly message | No | Yes |
| Lets the user try again | No | Yes |
Common gotchas and best practices
Do not use a bare except
Writing except: catches everything, including typos and interrupts you never meant to hide. It makes bugs very hard to find. Catch the specific type you expect instead.
# Avoid this
try:
do_something()
except:
pass # hides every possible errorDo not silently swallow errors
Using pass in an except block throws the error away with no trace. At the very least, print or log what happened so you know it occurred and can fix the cause later.
Keep the try block small
Guard only the line that can actually fail. A large try block may catch an exception from the wrong place and point you in the wrong direction while debugging.
Do not use exceptions for normal flow
Exceptions are for unexpected problems, not for ordinary conditions you can check with a simple if statement. Reserve them for the genuinely exceptional.
Recommendation and next steps
Here is the short version to remember:
- Wrap risky code in
try, recover inexcept. - Always catch specific exception types, never a bare
except. - Use
elsefor the success path andfinallyfor cleanup that must always run. - Use
raiseto reject bad data early with a clear message.
Good error handling is what separates a script that breaks in front of a user from one that keeps working. Practise by taking any small program you have written and asking, "What if the input is wrong?" Then guard those spots.
Want to go deeper with hands-on lessons and projects? Our free Python course walks you through exceptions, functions, files, and more, step by step.
Frequently Asked Questions
What is the difference between an error and an exception in Python?
In everyday speech people say "error", but Python's technical term for a runtime problem you can catch is an "exception". A syntax error stops your code from running at all, while an exception happens while the program is running and can be handled with try/except.
Should I ever use a bare except in Python?
Almost never. A bare except: catches every possible exception, including ones you did not anticipate, which hides bugs and makes debugging painful. Catch the specific type you expect, such as ValueError or FileNotFoundError.
What is the difference between else and finally?
The else block runs only when the try block finishes with no exception, so it holds your success code. The finally block runs no matter what happens — success or failure — which makes it ideal for cleanup like closing a file.
When should I use raise?
Use raise when your own code detects a condition that should not continue, such as invalid input to a function. Raising a clear exception like raise ValueError("Age cannot be negative.") stops bad data early and tells the caller exactly what went wrong.
Can one try block catch more than one exception type?
Yes. You can add multiple except blocks, one per type, and Python runs the first that matches. To handle several types the same way, group them in a tuple, like except (ValueError, TypeError):.
