Examples of error handling in Python: practical examples for real projects

If you write Python for anything serious—APIs, data pipelines, automation—you will hit errors. The difference between brittle scripts and production-quality code is how you handle those errors. In this guide, we’ll walk through real examples of error handling in Python: practical examples you can lift straight into your own projects. Instead of dry theory, you’ll see how try/except, custom exceptions, logging, and context managers behave in realistic situations. We’ll start with simple examples of catching common exceptions, then move into patterns you’ll actually use in 2024–2025: validating user input, working with files and networks, handling async tasks, and designing your own exception hierarchy. Along the way, you’ll see best practices that keep your code readable and debuggable without hiding real problems. If you’ve ever stared at a stack trace wondering what went wrong in production, these examples of error handling in Python will feel very familiar.
Written by
Jamie
Published

Let’s start with a few small, realistic snippets. These are the kinds of examples of error handling in Python: practical examples you’d actually paste into a script, not textbook toys.

Example: Safe integer conversion from user input

while True:
    raw = input("Enter an integer (or 'q' to quit): ")
    if raw.lower() == "q":
        break

    try:
        value = int(raw)
    except ValueError:
        print(f"'{raw}' is not a valid integer. Try again.")
        continue

    print(f"You entered: {value}")

This example of error handling keeps the program running when the user types bad input. Instead of crashing with a ValueError, it explains the problem and asks again.

Example: Handling missing files gracefully

from pathlib import Path

path = Path("config.json")

try:
    data = path.read_text(encoding="utf-8")
except FileNotFoundError:
    print("config.json not found. Using default configuration.")
    data = "{}"  # fallback

Here, error handling is about fallback behavior: if the file is missing, the script uses defaults instead of dying. This is one of the best examples of writing code that behaves predictably for users.


Core examples of error handling in Python: practical examples with try/except

At the heart of most examples of error handling in Python: practical examples is the try/except block. The trick is to be specific without being noisy.

Catching specific exceptions, not everything

def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return float("inf")  # or raise a custom error

print(divide(10, 2))   # 5.0
print(divide(10, 0))   # inf

Catching ZeroDivisionError makes the intent clear. Contrast that with the bad pattern:

## Avoid this pattern
try:
    result = a / b
except Exception:
    result = None

This swallows every error, including TypeError, NameError, or logic bugs. You’ll see this mistake in a lot of older code, but modern Python style guides (for example, PEP 8) recommend catching the narrowest exception you can.

Using else and finally for cleaner flow

else and finally are underused, but they show up in many good examples of error handling in Python.

from pathlib import Path

path = Path("data.csv")

try:
    content = path.read_text(encoding="utf-8")
except FileNotFoundError:
    print("data.csv missing")
else:
#    # Only runs if no exception
    print("File length:", len(content))
finally:
    print("Finished file operation")

This pattern is nice when you want a block that only runs on success (else) and a block that always runs (finally).


Real examples of Python error handling with files, APIs, and databases

Let’s move from toy snippets to real examples you’ll see in production scripts and services.

Handling network errors when calling an API

In 2024–2025, a lot of Python code is glued to web APIs. Network calls fail all the time: timeouts, DNS issues, rate limits. Here’s a realistic pattern using requests:

import requests
from requests.exceptions import HTTPError, Timeout, ConnectionError

API_URL = "https://api.example.com/users/123"

try:
    response = requests.get(API_URL, timeout=5)
    response.raise_for_status()  # may raise HTTPError
except Timeout:
    print("Request timed out. Try again later.")
except ConnectionError:
    print("Network problem. Check your internet connection.")
except HTTPError as exc:
    print(f"API returned error: {exc.response.status_code}")
else:
    user = response.json()
    print("User name:", user["name"])

This is one of the best examples of handling expected failure modes while still letting unexpected bugs bubble up.

For production services, you’d often combine this with logging and perhaps retry logic (with backoff) instead of just printing messages.

Validating JSON input in a web service

If you build APIs with frameworks like FastAPI or Flask, you’re constantly validating JSON and returning helpful error messages.

import json
from http import HTTPStatus

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/calculate")
def calculate():
    try:
        payload = request.get_json(force=True)
    except Exception:
#        # get_json can raise BadRequest internally
        return jsonify({"error": "Invalid JSON"}), HTTPStatus.BAD_REQUEST

    try:
        a = float(payload["a"])
        b = float(payload["b"])
    except (KeyError, TypeError, ValueError):
        return jsonify({"error": "Fields 'a' and 'b' must be numeric"}), HTTPStatus.BAD_REQUEST

    return jsonify({"result": a + b})

This is another concrete example of using exceptions to control flow while still giving clients clear feedback.


Designing your own exception classes: examples include data pipelines and APIs

As your codebase grows, custom exceptions make debugging far easier. These examples of error handling in Python: practical examples show how to organize them.

Simple custom exception hierarchy

class AppError(Exception):
    """Base class for application-specific errors."""


class ConfigError(AppError):
    pass


class DataValidationError(AppError):
    pass


def load_config(path):
    from pathlib import Path

    try:
        text = Path(path).read_text(encoding="utf-8")
    except FileNotFoundError as exc:
        raise ConfigError(f"Config file not found: {path}") from exc

#    # ... parse and validate config here ...

    return {}

Now your top-level code can handle all app-specific problems in one place:

try:
    config = load_config("config.toml")
except AppError as exc:
    print(f"Application error: {exc}")
#    # exit with a non-zero status code in real apps

In data engineering or analytics pipelines, this pattern is common. For example, a DataValidationError might carry context about which column failed validation.

Attaching context to errors

class DataValidationError(Exception):
    def __init__(self, message, row_number=None, column=None):
        super().__init__(message)
        self.row_number = row_number
        self.column = column


def validate_age(row, index):
    age = row.get("age")
    if age is None or age < 0 or age > 120:
        raise DataValidationError(
            "Invalid age value",
            row_number=index,
            column="age",
        )

This kind of structured error is one of the best examples of error handling that scales to large datasets and audit requirements.


Logging and monitoring: turning exceptions into useful signals

In 2024–2025, error handling is not just about catching exceptions; it’s about observability. You want errors to show up in logs, dashboards, or alerts.

Basic logging of exceptions

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
)
logger = logging.getLogger(__name__)


def process_item(item):
    try:
#        # risky operation
        return 10 / item["value"]
    except ZeroDivisionError:
        logger.warning("Value was zero; skipping item: %s", item)
    except KeyError:
        logger.error("Missing 'value' key in item: %s", item, exc_info=True)

Notice the exc_info=True flag: that includes the full traceback in the log, which is incredibly helpful in production.

Integrating with monitoring tools

Many teams send exceptions to monitoring services like Sentry or open-source alternatives. While vendor docs cover the details, the Python pattern is the same: catch, log, and re-raise or return a safe response.

For general guidance on system reliability and error monitoring, the NIST publications on system security and reliability are a good starting point, even though they’re not Python-specific.


Context managers and async: modern examples of error handling in Python

Modern Python code often uses context managers and async/await. These provide subtle but powerful ways to control error handling.

Using context managers to guarantee cleanup

from contextlib import contextmanager


@contextmanager
def open_db_connection(dsn):
    conn = connect_to_db(dsn)
    try:
        yield conn
    finally:
        conn.close()


## Usage
with open_db_connection("postgres://user:pass@localhost/db") as conn:
#    # Any exception here still triggers conn.close()
    run_query(conn, "DELETE FROM temp_table")

This pattern mirrors the standard with open(...) as f: file example, but in a database context. Even if an exception occurs inside the with block, conn.close() runs.

Async examples include timeouts and cancellation

Async code adds new failure modes: timeouts, cancellations, and concurrent errors.

import asyncio


async def fetch_data():
    await asyncio.sleep(2)
    return {"status": "ok"}


async def main():
    try:
        result = await asyncio.wait_for(fetch_data(), timeout=1)
    except asyncio.TimeoutError:
        print("fetch_data took too long and was canceled")
    else:
        print("Got result:", result)

asyncio.run(main())

This is a very real example of error handling in modern async Python: timeouts are treated as first-class events, not random crashes.

For more on concurrency models and their pitfalls, the University of Washington’s CSE courses and other .edu resources often have detailed lecture notes that pair well with these code examples.


Data validation and defensive programming: best examples in analytics code

If you work in data science or analytics, you know that messy inputs are the norm. Here’s how error handling pairs with validation.

Guarding against bad data in Pandas

import pandas as pd


def load_and_validate(path):
    try:
        df = pd.read_csv(path)
    except FileNotFoundError as exc:
        raise FileNotFoundError(f"Input file missing: {path}") from exc

    required = {"id", "age", "income"}
    missing = required - set(df.columns)
    if missing:
        raise ValueError(f"Missing required columns: {', '.join(sorted(missing))}")

    if (df["age"] < 0).any():
        raise ValueError("Age cannot be negative")

    return df

This function either returns a valid DataFrame or raises exceptions that clearly describe what went wrong. These are exactly the kind of examples of error handling in Python: practical examples that make pipeline failures diagnosable instead of mysterious.

For general thinking about data quality and validation, organizations like the National Institutes of Health (NIH) publish guidance on data integrity, which, while not Python-specific, aligns well with this defensive style.


FAQ: short answers with examples

What are some common examples of error handling in Python?

Common examples include:

  • Wrapping file access in try/except FileNotFoundError and falling back to defaults.
  • Validating user input and catching ValueError when casting types.
  • Handling requests timeouts and HTTPError when calling web APIs.
  • Using custom exceptions like DataValidationError to flag bad data.
  • Managing resources with with blocks so cleanup happens even on errors.

These real examples of error handling in Python show up in scripts, web services, and data pipelines.

Can you show an example of catching multiple exception types?

try:
    value = float(user_input)
except (TypeError, ValueError):
    print("Please enter a valid number.")

Grouping related exceptions like this is common in practical code.

When should I create a custom exception class?

Create one when you want to:

  • Represent a domain-specific problem (like PaymentFailedError).
  • Group several low-level errors under one high-level error.
  • Attach structured context (IDs, row numbers, etc.) to an error.

That’s why many of the best examples of error handling in Python in larger projects use a small, well-thought-out exception hierarchy.

Is it okay to use a bare except in Python?

Almost never. A bare except: catches KeyboardInterrupt and SystemExit, which you usually want to propagate. Prefer except Exception: at minimum, and ideally a specific exception type. Most style guides and Python courses at universities (for example, material from Harvard’s CS50) strongly recommend avoiding bare except blocks.

How do I decide whether to handle an error or re-raise it?

Ask yourself:

  • Can this layer of the code fix the problem or offer a reasonable fallback?
  • Can it at least add context (file name, user ID, request path) before re-raising?

If the answer is no, let the exception propagate. Many real examples of error handling in Python show a pattern of catching, logging or enriching the error, then re-raising for a higher layer (like a web framework) to turn into a user-facing response.

Explore More Python Code Snippets

Discover more examples and insights in this category.

View All Python Code Snippets