Errors and try/except
Some errors are your own typing mistakes, and the cure is to fix the code. Others are not your fault at all: the user types "ten" where a number is needed, a file has been deleted, the divisor turns out to be zero. A good program expects these and handles them. Python's tool for this is try and except.
What an error looks like
When Python cannot carry out a line, it raises an exception. If nothing handles it, the program stops and prints a traceback.
number = int("ten")
print("This line never runs")What Python says
Traceback (most recent call last):
File "example.py", line 1, in <module>
number = int("ten")
ValueError: invalid literal for int() with base 10: 'ten'The last line of the traceback has two parts: the type of the exception, here ValueError, and a message. The type is what you will catch.
Catching an error
Put the risky lines in a try block. Put what to do if they fail in an except block.
text = "ten"
try:
number = int(text)
print("Twice that is", number * 2)
except ValueError:
print("That is not a whole number.")
print("The program carries on.")Output
That is not a whole number.
The program carries on.Python tries the first block. The moment a line raises a ValueError, it jumps to the except block, skipping the rest of the try. If no error happens, the except block is skipped.
Asking until the answer is good
Combine try with the while True loop from lesson 9. The person here typed abc, then 4.5, then 12.
while True:
try:
age = int(input("Your age? "))
break
except ValueError:
print("Please type a whole number.")
print("Age:", age)Output
Your age? abc
Please type a whole number.
Your age? 4.5
Please type a whole number.
Your age? 12
Age: 12The break is reached only when int succeeds. This pattern belongs in every program that asks for a number.
Different errors, different answers
One try can have several except blocks. Python uses the first one that matches.
def divide(a, b):
try:
return int(a) / int(b)
except ValueError:
return "Both values must be numbers."
except ZeroDivisionError:
return "Cannot divide by zero."
print(divide("10", "4"))
print(divide("10", "x"))
print(divide("10", "0"))Output
2.5
Both values must be numbers.
Cannot divide by zero.To treat two kinds the same way, list them in brackets: except (ValueError, ZeroDivisionError):.
Reading the error message
Add as e to get the exception itself, which holds the message.
try:
marks = [70, 80]
print(marks[5])
except IndexError as e:
print("Problem:", e)Output
Problem: list index out of rangeCommon exception types
| Exception | Raised when |
|---|---|
ValueError | the type is right but the value is not, such as int("ten") |
TypeError | the type is wrong, such as "5" + 5 |
ZeroDivisionError | you divide by zero |
IndexError | a list position does not exist |
KeyError | a dictionary key does not exist |
FileNotFoundError | the file to read is not there |
NameError | a name has no value yet |
Each one has its own page in the errors section.
else and finally
Two more optional blocks complete the picture.
elseruns only if thetryblock finished without an error.finallyruns always, error or not. It is the place for tidying up.
def read_number(text):
try:
value = float(text)
except ValueError:
print("Not a number:", text)
else:
print("Got", value)
finally:
print("Checked", repr(text))
read_number("3.5")
read_number("abc")Output
Got 3.5
Checked '3.5'
Not a number: abc
Checked 'abc'A missing file
try:
with open("settings.txt", encoding="utf-8") as f:
settings = f.read()
except FileNotFoundError:
settings = "default"
print("No settings file found. Using defaults.")
print(settings)Output
No settings file found. Using defaults.
defaultThis is a natural use: try the normal thing, and fall back to something sensible when it is not possible.
Do not catch everything
It is tempting to write a bare except: that catches every error. Do not. It also hides your own bugs, such as a misspelt variable name, and the program fails silently in ways that take hours to find. Catch the specific exceptions you expect and let the rest stop the program, so that you see them.
Also keep the try block small. Put in it only the lines that can raise the error you are handling.
Raising your own errors
Your functions can raise exceptions too, with the keyword raise. Do this when a function is given a value it cannot sensibly work with.
def percentage(marks, total):
if total <= 0:
raise ValueError("total must be more than zero")
return marks / total * 100
try:
print(percentage(45, 0))
except ValueError as e:
print("Error:", e)Output
Error: total must be more than zeroRaising an error is better than returning a strange value such as -1, because the caller cannot ignore it by accident.
Check first, or try?
For simple cases a plain if is clearer: if b != 0: before dividing. For anything where checking in advance is hard, such as whether a string is a valid number or whether a file can be opened, try is the Python way.
Try it yourself
Exercise 1. Convert the string "12a" to an integer inside a try. If it fails, print Invalid number.
Show one answer
try:
n = int("12a")
print(n)
except ValueError:
print("Invalid number")Output
Invalid numberExercise 2. Write a function safe_divide(a, b) that returns the result, or None if b is zero. Test both cases.
Show one answer
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
print(safe_divide(10, 4), safe_divide(10, 0))Output
2.5 NoneExercise 3. A dictionary holds {"tea": 12, "lassi": 40}. Look up "coffee" inside a try and print Not on the menu if the key is missing.
Show one answer
menu = {"tea": 12, "lassi": 40}
try:
print(menu["coffee"])
except KeyError:
print("Not on the menu")Output
Not on the menuExamples on this page were run with Python 3.14 on 12 November 2026. The output shown is the real output.
This is an independent learning site. It is not connected with or approved by the Python Software Foundation. "Python" is a trademark of the Python Software Foundation and is used here only to name the language.