Python Exception Handling Tutorial: try, except, else and finally
A program that works on your own test data can still break the moment a real user touches it. Someone types a word where you expected a number. A file is missing. A network call times out. A list is shorter than you thought. Without any protection, Python stops at the first problem and prints a long message that means nothing to the user.
Exception handling is how you stay in control when this happens. You tell Python which lines might fail, what to do if they do, and what must always run afterward, such as closing a file. Done well, a program turns a crash into a clear message or a safe fallback, and it keeps running.
In this tutorial, we explain what an error and an exception are, and how Python reacts when one is not handled. We then go through the try and except blocks, handling more than one error, the else and finally blocks, the raise statement, custom exceptions, and file errors. Every program has been run. We finish with the order of except blocks, the exception hierarchy, and the mistakes to avoid.
What is an Error in Python?
Python has two kinds of errors.
Syntax errors are mistakes in how the code is written, such as a missing bracket. Python finds them before the program runs, and the program does not start at all.
Exceptions are errors that happen while the program is running, even though the code is written correctly. Dividing by zero is one example. Python stops the current line, shows a message, and ends the program, unless you handle it.
python
try:
compile("print('hello'", "<test>", "exec")
except SyntaxError as e:
print("SyntaxError:", e.msg)
output
SyntaxError: '(' was never closed
The difference matters, because try and except can handle exceptions, and a syntax error is something you fix in the code.
Common Built In Exceptions
These are the exceptions you will meet most, each with the real message Python gives.
Common exceptions and their messages
Exception
When it happens
Real message
ZeroDivisionError
Dividing by zero
division by zero
NameError
Using a name that does not exist
name ‘undefined_name’ is not defined
IndexError
A list position is out of range
list index out of range
KeyError
A dictionary key is missing
‘b’
ValueError
Right type, wrong value
invalid literal for int() with base 10: ‘abc’
TypeError
Wrong type for an operation
can only concatenate str (not “int”) to str
FileNotFoundError
The file does not exist
[Errno 2] No such file or directory
The Effect of Exception Handling
An exception changes the normal flow of a program. When one is raised, Python stops running the lines in order and looks for a handler. If it finds an except block that matches, it runs that block and carries on after the whole try statement. If it finds none, the program ends right away and prints a traceback.
How Python Handles an Exception
Python reads the try block line by line. If no line fails, the except blocks are skipped. If a line fails, Python jumps out of the try block at once, and the lines after the failing one do not run. It then picks the first except block that matches the error type.
Handling exceptions gives you three real benefits.
Cleaner code. The normal path and the error path are separate, so you do not wrap every line in an if check.
One consistent way to deal with errors. Every library you use reports problems in the same way.
A program that does not crash. It can show a clear message, use a default, or try again.
Exception Handling Example
The first print works. The second asks for index 4 in a list that has only four items, so Python raises an IndexError, and the except block runs.
python
test = [1, 2, 4, 8]
try:
print("First element = %d" % (test[0]))
# Only 4 elements, so index 4 raises an IndexError
print("Fifth element = %d" % (test[4]))
except IndexError:
print("Error occurred")
output
First element = 1
Error occurred
Notice that the first line printed before the error, because the lines before the failure run normally.
Reading the Error Message
You can catch the error as a variable with the word as, and print the message Python gives. This helps when you need to know what went wrong.
python
try:
int("abc")
except ValueError as e:
print("Error:", e)
output
Error: invalid literal for int() with base 10: 'abc'
Multiple Exception Handling Example
A block of code can fail in more than one way. You have two choices. You can catch several types in one except line, by putting them in round brackets as a tuple. Or you can write a separate except block for each type, so each error gets its own response.
One Handler for Several Errors
The brackets are required. This example can fail in both ways. For the value 5, the division is by zero. For the text seven, Python cannot subtract a number from text.
python
def calculate(vari):
try:
cal = 100 / (vari - 5)
print("value of cal =", cal)
except (ZeroDivisionError, TypeError):
print("Error handled")
calculate(5)
calculate(7)
calculate("seven")
output
Error handled
value of cal = 50.0
Error handled
A Separate Handler for Each Error
When the right response is different for each error, give each its own block. Python checks them from top to bottom and runs only the first match.
python
def divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("You cannot divide by zero")
except TypeError:
print("Both values must be numbers")
else:
print("Result:", result)
finally:
print("Division attempted")
divide(10, 2)
divide(10, 0)
divide(10, "2")
output
Result: 5.0
Division attempted
You cannot divide by zero
Division attempted
Both values must be numbers
Division attempted
At most one except block runs for each error.
The else and finally Statements
Two optional blocks make a try statement more useful.
else and finally
Block
When it runs
Typical use
else
Only when the try block raised no exception
Code that should run only if everything worked
finally
Always, whether or not there was an error
Cleanup, such as closing a file or a connection
The function below turns an age into days. For 20, nothing fails, so else and finally both run. For 20.5, the conversion to an int raises a ValueError, so the except block runs and then finally runs.
python
def print_age_in_days(years):
print("Your age in days is about", 365 * int(years))
def check_age(age):
try:
print_age_in_days(age)
except ValueError:
print("You did not enter an age as a whole number")
else:
print(age, "successfully converted into days")
finally:
print("Input test complete")
check_age("20")
check_age("20.5")
output
Your age in days is about 7300
20 successfully converted into days
Input test complete
You did not enter an age as a whole number
Input test complete
In a real program the age would come from the input function. We pass it in as text here so the example runs without waiting for typing.
Raise an Exception in Python
So far Python raised the errors. You can also raise one yourself, with the raise statement. This is how you reject bad data early, with a clear message, before it causes a confusing problem later.
The raise Statement
python
def set_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
return age
try:
set_age(-5)
except ValueError as e:
print("Error:", e)
output
Error: Age cannot be negative
The plainest form raises a NameError by hand, just to show the syntax:
python
try:
raise NameError("Python code")
except NameError as e:
print("Exception:", e)
output
Exception: Python code
Raising the Same Error Again
A bare raise, with nothing after it, inside an except block, sends the same exception up to the next handler. If there is no outer handler, Python runs the inner block and then stops with a traceback ending in NameError: Python code. With an outer handler, it works as you would expect:
Inner handler ran
Outer handler caught: Python code
Use this when a function wants to log or clean up after an error, and still let the caller decide what to do.
Create Your Own Exception
When the built in exceptions do not describe your problem, make your own by writing a class that inherits from Exception. It gives your error a clear name that the rest of the program can catch.
python
class LowBalanceError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise LowBalanceError(f"Balance {balance} is less than {amount}")
return balance - amount
try:
withdraw(500, 800)
except LowBalanceError as e:
print("Custom error:", e)
output
Custom error: Balance 500 is less than 800
Opening Files and Exceptions
Files are the most common place where exceptions happen, because a file can be missing, locked, or without permission. Opening a file that does not exist raises a FileNotFoundError. This is a kind of OSError. In Python 3, the name IOError is only another name for OSError.
The error object carries useful details. The attribute errno is the error number, strerror is the message, and filename is the file that caused it.
python
try:
infile = open("Incorrect filename")
except OSError as ioe:
print("Unable to open the file")
print("Error number", ioe.errno)
print("Message", ioe.strerror)
print("Filename in error", ioe.filename)
output
Unable to open the file
Error number 2
Message No such file or directory
Filename in error Incorrect filename
Use with to Close Files Safely
The with statement closes the file for you, even when an error happens, so you do not need a finally block just for that.
python
try:
with open("notes_missing.txt") as f:
data = f.read()
except FileNotFoundError:
print("File not found, using default text")
output
File not found, using default text
The Exception Hierarchy
Exceptions are arranged in a family tree. Every exception inherits from BaseException. Almost all the errors you meet inherit from Exception, which sits just below it. So Exception is not the top of the tree.
The first line reads from the left: ZeroDivisionError is a kind of ArithmeticError, which is a kind of Exception, which is a kind of BaseException. KeyError and IndexError are both kinds of LookupError. That means catching the parent catches all its children.
Why the Order of except Blocks Matters
Python runs the first except block that matches. If a parent comes before its child, the child block can never run.
python
try:
[1, 2][5]
except LookupError:
print("Caught by the parent first")
except IndexError:
print("Never reached")
output
Caught by the parent first
Put the most specific exception first, and the general ones after.
Best Practices and Common Mistakes
Do not use a bare except. An except line with no type catches everything, even a user pressing Ctrl and C, and hides real bugs. Name the errors you expect.
Keep the try block small. Put only the risky lines inside it, so you know which line failed.
Do not silence errors. An empty except block with only pass makes bugs invisible. At least print or log the message.
Catch the specific error. Use ValueError for bad input and FileNotFoundError for a missing file, not the general Exception.
Use finally or with for cleanup. Files and connections should close, even when something fails.
Order the except blocks from specific to general.
Raise your own errors early. Check the input at the start of a function and raise a clear error, instead of letting it fail somewhere confusing.
Watch the spelling and the colon. Writing exceptValueError, or leaving the colon off an except line, is a syntax error, and Python will not even start the program.
Quick Reference
Exception handling keywords
Keyword
What it does
try
Holds the code that might fail
except
Runs if a matching error happens, and can name the error type
else
Runs only if the try block had no error
finally
Always runs, used for cleanup
raise
Raises an exception yourself, or raises the current one again
as
Gives a name to the error object, such as except ValueError as e
Complete Script to Run Yourself
Here is the full script used to produce every output on this page. Copy it into a file, run it with Python 3, and compare the results line by line.
python
# 1. The basic try and except
test = [1, 2, 4, 8]
try:
print("First element = %d" % (test[0]))
# Only 4 elements, so index 4 raises an IndexError
print("Fifth element = %d" % (test[4]))
except IndexError:
print("Error occurred")
# 2. What an unhandled error looks like (we catch it only to show the message)
for label, func in [
("ZeroDivisionError", lambda: 10 / 0),
("NameError", lambda: undefined_name),
("IndexError", lambda: [1, 2, 3][5]),
("KeyError", lambda: {"a": 1}["b"]),
("ValueError", lambda: int("abc")),
("TypeError", lambda: "5" + 5),
("FileNotFoundError", lambda: open("no_such_file.txt")),
]:
try:
func()
except Exception as e:
print(label, "->", e)
# 3. Handling several exceptions
def calculate(vari):
try:
cal = 100 / (vari - 5)
print("value of cal =", cal)
except (ZeroDivisionError, TypeError):
print("Error handled")
calculate(5)
calculate(7)
calculate("seven")
# 4. Separate handlers for separate errors
def divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("You cannot divide by zero")
except TypeError:
print("Both values must be numbers")
else:
print("Result:", result)
finally:
print("Division attempted")
divide(10, 2)
divide(10, 0)
divide(10, "2")
# 5. else and finally
def print_age_in_days(years):
print("Your age in days is about", 365 * int(years))
def check_age(age):
try:
print_age_in_days(age)
except ValueError:
print("You did not enter an age as a whole number")
else:
print(age, "successfully converted into days")
finally:
print("Input test complete")
check_age("20")
check_age("20.5")
# 6. The raise statement
try:
raise NameError("Python code")
except NameError as e:
print("Exception:", e)
# 7. Raising again with a bare raise, caught by an outer handler
try:
try:
raise NameError("Python code")
except NameError:
print("Inner handler ran")
raise
except NameError as e:
print("Outer handler caught:", e)
# 8. Raising your own error with a check
def set_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
return age
try:
set_age(-5)
except ValueError as e:
print("Error:", e)
# 9. Custom exception
class LowBalanceError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise LowBalanceError(f"Balance {balance} is less than {amount}")
return balance - amount
try:
withdraw(500, 800)
except LowBalanceError as e:
print("Custom error:", e)
# 10. Opening files
try:
infile = open("Incorrect filename")
except OSError as ioe:
print("Unable to open the file")
print("Error number", ioe.errno)
print("Message", ioe.strerror)
print("Filename in error", ioe.filename)
print(IOError is OSError)
print(FileNotFoundError.__mro__[1].__name__)
# 11. with closes the file for you
try:
with open("notes_missing.txt") as f:
data = f.read()
except FileNotFoundError:
print("File not found, using default text")
# 12. The exception hierarchy
print(ZeroDivisionError.__mro__)
print(Exception.__mro__)
print(issubclass(KeyError, LookupError), issubclass(IndexError, LookupError))
# 13. Order of except blocks matters
try:
[1, 2][5]
except LookupError:
print("Caught by the parent first")
except IndexError:
print("Never reached")
# 14. Catching every error, and why not to
try:
int("abc")
except Exception as e:
print(type(e).__name__, "-", e)
# 15. Getting the full traceback text
import traceback
try:
1 / 0
except ZeroDivisionError:
print(traceback.format_exc().strip().splitlines()[-1])
# 16. Syntax errors are not caught at run time
try:
compile("print('hello'", "<test>", "exec")
except SyntaxError as e:
print("SyntaxError:", e.msg)
Exception handling is less about stopping errors and more about deciding what happens next. Catch the specific errors you expect, give the user a clear message, clean up with finally or with, and raise your own errors when the data is wrong. Run the script above, break each example on purpose, and read the messages. Then move on to Python file handling and modules, where these habits are used all the time.
00
Popular Tags:
Best Python Training in BTM Layout
Python Exception Handling Tutorial
python training in btm