Python Full Tutorial — Part 9: Error Handling

By Suraj Ahir January 02, 2026 6 min read

Python — Python Modules
Python — Python Modules
← Part 8 Python Tutorial · Part 9 of 12 Part 10 →

Real programs fail. Not because programmers are incompetent — because the real world is unpredictable. A file that should exist does not. A user enters text where a number was expected. A network request times out. A database is unavailable. Professional programs handle these failures gracefully instead of crashing.

What Happens Without Error Handling

Unhandled Error
number = int(input("Enter a number: "))
# User types "hello" → Program crashes with ValueError

try-except — Catching Errors

Basic try-except
try:
    number = int(input("Enter a number: "))
    result = 100 / number
    print(f"Result: {result}")
except ValueError:
    print("That was not a valid number.")
except ZeroDivisionError:
    print("Cannot divide by zero.")

except Exception — Catching All Errors

Catch All Exceptions
try:
    risky_code()
except Exception as e:
    print(f"Something went wrong: {e}")

else and finally

Full try-except-else-finally
try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("File does not exist.")
else:
    print("File read successfully!")
    print(content)
finally:
    print("This runs always — cleanup goes here")
    # Close connections, release resources, etc.

Common Python Exceptions

Raising Your Own Exceptions

raise Statement
def set_age(age):
    if not isinstance(age, int):
        raise TypeError("Age must be an integer")
    if age < 0 or age > 150:
        raise ValueError("Age must be between 0 and 150")
    return age

try:
    set_age(-5)
except ValueError as e:
    print(f"Invalid age: {e}")

Custom Exception Classes

Custom Exception
class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(f"Need {amount}, only have {balance}")

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

try:
    new_balance = withdraw(500, 1000)
except InsufficientFundsError as e:
    print(f"Transaction failed: {e}")

In Part 10, we explore modules and packages — how Python code is organized into reusable, importable units.

Common Mistakes Beginners Make

Learning from common pitfalls saves hours of frustration. Here are the mistakes most beginners make in this area and how to avoid them.

The first mistake is trying to memorize syntax before understanding logic. Python syntax is simple — you could memorize it in a day. But if you do not understand why you are writing what you are writing, you will not be able to adapt when things change or when a problem looks slightly different. Always ask: what problem does this solve?

The second mistake is writing very long functions or very long scripts without breaking them into logical units. Real professional code is made of small, focused pieces that each do one thing well. The moment your code does two or three unrelated things in one block, it is time to split it up.

The third mistake is not reading error messages. Python's error messages are actually quite good. They tell you the file, the line number, the type of error, and often a description. Read the entire error before searching online. The answer is usually right there.

How This Topic Appears in Real Projects

In real codebases, every concept from this tutorial series appears constantly. Backend web applications built with Flask or Django use functions, classes, data structures, and error handling throughout every route and service. Data pipelines use loops and comprehensions to process thousands of records efficiently. CLI tools use argument parsing, file handling, and process management. DevOps automation scripts combine shell integration with Python logic to orchestrate deployments, monitor systems, and handle alerts.

The concepts that feel abstract right now will click into place the moment you start building something real. That is why the best way to learn is to pick a small project that solves a problem you actually have — even something simple like a personal expense tracker or a file organizer — and build it using everything you have learned so far.

Practice Exercise

Before moving to the next part, write a small program that uses what you learned in this section. Do not copy from anywhere. Start with a blank file and build it from memory. The struggle is the learning. If you get stuck, read the code examples again — do not just copy them. Understand each line, then close the examples and write the program yourself. This is how programming actually sticks.

Creating Custom Exceptions

While Python's built-in exceptions cover many cases, real applications benefit from custom exception classes that carry meaningful names and can include domain-specific information:

Custom Exception Classes
class InsufficientFundsError(Exception):
    # Raised when a withdrawal exceeds the account balance.
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(
            f"Cannot withdraw Rs {amount}. Current balance: Rs {balance}"
        )

class BankAccount:
    def __init__(self, balance):
        self.balance = balance
    
    def withdraw(self, amount):
        if amount > self.balance:
            raise InsufficientFundsError(self.balance, amount)
        self.balance -= amount
        return self.balance

# Using the custom exception
account = BankAccount(1000)
try:
    account.withdraw(1500)
except InsufficientFundsError as e:
    print(f"Transaction failed: {e}")
    print(f"Attempted: Rs {e.amount}, Available: Rs {e.balance}")

Custom exceptions make your code's error conditions explicit and self-documenting. Anyone reading code that raises InsufficientFundsError immediately understands what went wrong without reading the error message text.

Logging vs Printing

In production code, using Python's logging module instead of print() for error reporting and debugging is strongly recommended. Logging provides severity levels (DEBUG, INFO, WARNING, ERROR, CRITICAL), timestamps, file output, and configurable verbosity — capabilities that print statements cannot provide. Learning to use the logging module is an important step toward writing production-quality Python code.

Practice Exercise

Create a simple bank account class with deposit and withdrawal methods. Define custom exceptions for invalid amounts (negative numbers), insufficient funds, and account closure. Write a test program that exercises each exception path and handles them appropriately, printing informative messages for each case.

Disclaimer: This content is for educational purposes only. SRJahir Tech does not guarantee any specific outcome, job placement, or exam result. Learning requires consistent effort and practical application.