Skip to content

Exception Handling in Python

Mastering Exception Handling in Python: A Comprehensive Guide

Section titled “Mastering Exception Handling in Python: A Comprehensive Guide”

Errors are unavoidable in real programs — a file may be missing, a user may type text where a number was expected, or a network call may fail. Exception handling is the mechanism Python provides to detect these errors and respond to them gracefully instead of crashing. In this guide, we’ll explore the try, except, else, and finally blocks, the raise keyword, and how to build your own custom exceptions.

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. When Python encounters an error it cannot handle, it raises an exception. If the exception is not handled, the program stops and prints a traceback.

error.py
# An unhandled exception
print(10 / 0)

Output:

command
C:\Users\Your Name> python error.py
Traceback (most recent call last):
  File "error.py", line 2, in <module>
    print(10 / 0)
ZeroDivisionError: division by zero

The program never reaches any code after line 2 — the ZeroDivisionError terminates it. Exception handling lets us catch that error and continue.

  • Syntax errors are mistakes in the structure of the code (a missing colon, an unclosed bracket). They are caught before the program runs and cannot be handled at runtime.
  • Exceptions are raised while the program runs. These are the errors we handle with try/except.
syntax_error.py
# Syntax error - cannot be caught with try/except
if True
    print("missing colon")

The try block contains code that might raise an exception. The except block contains the code that runs if an exception occurs.

The syntax of the try-except block in Python is as follows:

Syntax
try:
    # code that might raise an exception
except ExceptionType:
    # code that runs if the exception occurs

Diagram:

diagram try-except flow mermaid
Flow of the try-except block in Python

The following example demonstrates how to catch a ZeroDivisionError:

try_except.py
try:
    result = 10 / 0
    print(result)
except ZeroDivisionError:
    print("You cannot divide by zero!")

Output:

command
C:\Users\Your Name> python try_except.py
You cannot divide by zero!

In the above example, the division raises a ZeroDivisionError. Python jumps to the matching except block, prints the message, and the program continues normally instead of crashing.

You can capture the exception object itself using the as keyword. This gives you access to the error message.

as_keyword.py
try:
    number = int("hello")
except ValueError as error:
    print("Conversion failed:", error)

Output:

command
C:\Users\Your Name> python as_keyword.py
Conversion failed: invalid literal for int() with base 10: 'hello'

A single try block can be followed by several except blocks. Python runs the first one that matches the raised exception.

multiple.py
try:
    value = int(input("Enter a number: "))
    result = 100 / value
    print(result)
except ValueError:
    print("That was not a valid number.")
except ZeroDivisionError:
    print("You cannot divide by zero.")

You can also handle several exception types in one block by passing a tuple:

tuple_except.py
try:
    risky_operation()
except (ValueError, TypeError, KeyError) as error:
    print("Something went wrong:", error)

The else block runs only if no exception was raised in the try block. It is useful for code that should run after a successful try, keeping the try block focused on the risky operation alone.

else_clause.py
try:
    number = int("42")
except ValueError:
    print("Invalid number.")
else:
    print("Conversion succeeded:", number)

Output:

command
C:\Users\Your Name> python else_clause.py
Conversion succeeded: 42

The finally block runs no matter what — whether or not an exception was raised, and even if a return statement executes. It is used for cleanup tasks such as closing files or releasing resources.

finally_clause.py
try:
    file = open("data.txt", "r")
    data = file.read()
except FileNotFoundError:
    print("File not found.")
finally:
    print("Cleaning up...")

Diagram:

diagram try-except-else-finally mermaid
Full exception handling flow in Python

You can deliberately raise an exception using the raise keyword. This is useful for signalling that something is wrong with the input to your function.

raise.py
def set_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative")
    print("Age set to", age)
 
set_age(-5)

Output:

command
C:\Users\Your Name> python raise.py
Traceback (most recent call last):
  ...
ValueError: Age cannot be negative

Inside an except block, a bare raise re-raises the current exception after you have done some work (such as logging).

reraise.py
try:
    risky()
except ValueError:
    print("Logging the error before re-raising...")
    raise

You can define your own exception types by subclassing the built-in Exception class. Custom exceptions make your code more readable and let callers catch your specific error.

custom_exception.py
class InsufficientFundsError(Exception):
    """Raised when a withdrawal exceeds the available balance."""
    pass
 
def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(
            f"Cannot withdraw {amount}; balance is only {balance}"
        )
    return balance - amount
 
try:
    withdraw(100, 250)
except InsufficientFundsError as error:
    print("Transaction failed:", error)

Output:

command
C:\Users\Your Name> python custom_exception.py
Transaction failed: Cannot withdraw 250; balance is only 100
ExceptionRaised when
ValueErrorA function gets an argument of the right type but wrong value (e.g. int("abc")).
TypeErrorAn operation is applied to an object of the wrong type.
KeyErrorA dictionary key is not found.
IndexErrorA sequence index is out of range.
FileNotFoundErrorA file or directory is requested but does not exist.
ZeroDivisionErrorThe second argument of a division or modulo is zero.
AttributeErrorAn attribute reference or assignment fails.
ImportErrorAn import statement fails to find the module.

Exception handling lets your programs respond to errors gracefully instead of crashing. The try block holds risky code, except handles failures, else runs on success, and finally always runs for cleanup. The raise keyword lets you signal errors, and custom exception classes make your error handling expressive and precise. Master these tools and your Python programs will be far more robust. For more hands-on examples and in-depth tutorials, explore our resources on Python Central Hub!


Exercise 2 – try / except / else / finally

Section titled “Exercise 2 – try / except / else / finally”

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading