Practical examples of handling multiple exceptions in Python

If you write Python for more than a week, you’ll hit the moment where one `try` block needs to catch several different things that can go wrong. That’s where good examples of handling multiple exceptions in Python become incredibly useful. Instead of piling on messy nested `try/except` blocks, you can structure your error handling so it’s readable, predictable, and easy to debug. In this guide, we’ll walk through real examples of handling multiple exceptions in Python examples drawn from everyday tasks: parsing user input, hitting APIs, working with files, and dealing with databases. You’ll see how to catch several exception types in one line, how to group related errors, and when it’s better to split them apart. We’ll also look at patterns that professional teams use in 2024–2025 to keep their error handling sane in production code. By the end, you’ll have a set of patterns you can copy, adapt, and drop straight into your own projects.
Written by
Jamie
Published

Real-world examples of handling multiple exceptions in Python

Let’s skip the theory and start with concrete code. When people search for examples of handling multiple exceptions in Python examples, they’re usually fighting with something like this:

try:
    user_age = int(input("Age: "))
    years_to_retirement = 65 / user_age
except ValueError:
    print("Please enter a valid number.")
except ZeroDivisionError:
    print("Age cannot be zero.")

It works, but it gets noisy fast once you add more branches. Python gives you several cleaner patterns for handling multiple exceptions in the same try block.


Grouping exception types in a single except clause

The most common example of handling multiple exceptions is grouping related ones into a tuple. This keeps your code short while still being explicit.

try:
    value = float(input("Enter a price: "))
except (ValueError, TypeError):
    print("Invalid numeric value.")

This pattern shines when different exception types should trigger the same response. In production code, you often see examples of handling multiple exceptions in Python examples like this around input validation or API parsing.

Example: Parsing JSON from a web API

Imagine a script that calls a REST API and parses JSON. Several things can blow up:

  • The HTTP request fails
  • The response isn’t valid JSON
  • The JSON is valid but missing the key you expected

Here’s a compact way to handle all three:

import json
import requests

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

try:
    response = requests.get(API_URL, timeout=5)
    response.raise_for_status()
    data = response.json()
    username = data["username"]
except (requests.exceptions.Timeout,
        requests.exceptions.ConnectionError) as network_err:
    print(f"Network error: {network_err}")
except (json.JSONDecodeError, ValueError) as parse_err:
    print(f"Response was not valid JSON: {parse_err}")
except KeyError as key_err:
    print(f"Missing expected field: {key_err}")

This is one of the best examples of handling multiple exceptions in Python examples for API work: you group low-level network issues together, group parsing issues together, and keep business-logic errors (like missing keys) separate.

For deeper background on HTTP error codes and behavior, the official HTTP specifications are maintained by the IETF at ietf.org.


Handling multiple exceptions with shared logging but different messages

Sometimes you want to log all errors in the same place, but still react differently depending on what went wrong. A common pattern in modern (2024–2025) codebases is to use one logging function and several specific except blocks.

Example: File processing with different recovery paths

You’re processing a CSV file uploaded by a user. Several problems are common:

  • The file doesn’t exist
  • The user doesn’t have read permission
  • The CSV is malformed
import csv
import logging

logging.basicConfig(level=logging.INFO)

FILE_PATH = "uploads/data.csv"

try:
    with open(FILE_PATH, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        rows = list(reader)
except FileNotFoundError as e:
    logging.error("File not found: %s", e)
    print("We couldn't find your file. Please upload it again.")
except PermissionError as e:
    logging.error("Permission error: %s", e)
    print("The server cannot read that file. Contact support.")
except csv.Error as e:
    logging.error("CSV parsing error: %s", e)
    print("Your file is not a valid CSV.")

In many real examples of handling multiple exceptions in Python examples, you’ll see this pattern: each exception type gets its own user-facing message, but the logging behavior is consistent.


Using as to inspect multiple exceptions in one handler

Grouping exceptions with a tuple is handy, but sometimes you still want to inspect which specific type you got. You can do that by catching a tuple and then checking isinstance.

Example: Normalizing different numeric errors

You’re building a small calculator API and want to return the same HTTP 400 error for both invalid input and division by zero, but still log the exact cause.

import logging

logging.basicConfig(level=logging.INFO)


def safe_divide(a_str: str, b_str: str) -> float | None:
    try:
        a = float(a_str)
        b = float(b_str)
        return a / b
    except (ValueError, ZeroDivisionError) as err:
        if isinstance(err, ValueError):
            logging.warning("Invalid numeric input: %s", err)
        elif isinstance(err, ZeroDivisionError):
            logging.warning("Division by zero: %s", err)
        return None


result = safe_divide("10", "0")

This style shows up in some of the best examples of handling multiple exceptions in Python examples for APIs: the public behavior is unified, while internal logging stays precise.


Modern async examples of handling multiple exceptions in Python

Async code introduces new failure modes: timeouts, cancellations, and concurrency issues. In 2024–2025, most serious Python backends use asyncio or frameworks like FastAPI, so it’s worth seeing how multiple exceptions work there.

Example: Async HTTP call with timeout and cancellation

import asyncio
import aiohttp

async def fetch_json(url: str):
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url, timeout=3) as resp:
                resp.raise_for_status()
                return await resp.json()
    except (aiohttp.ClientError, asyncio.TimeoutError) as net_err:
#        # Network or timeout problems
        print(f"Network/timeout error: {net_err}")
    except asyncio.CancelledError:
#        # Task was cancelled by caller
        print("Request was cancelled.")
        raise  # Important: re-raise cancellations in async code


asyncio.run(fetch_json("https://api.example.com/data"))

This is a realistic example of handling multiple exceptions in async Python: networking issues are grouped together, but CancelledError is treated separately and re-raised, which is considered good practice in modern asyncio code.

The official Python documentation has more on asyncio error handling at docs.python.org.


Layered exception handling: catching once, handling many

Sometimes the right move is to catch exceptions in a lower-level helper, normalize them, and then handle that one custom exception type in higher layers. This keeps your app from being littered with low-level exceptions like OSError or requests.Timeout.

Example: Wrapping multiple low-level exceptions in a custom one

class DataFetchError(Exception):
    """High-level error for data fetching problems."""


def fetch_user_profile(user_id: int) -> dict:
    import requests

    try:
        resp = requests.get(f"https://api.example.com/users/{user_id}", timeout=5)
        resp.raise_for_status()
        return resp.json()
    except (requests.exceptions.RequestException, ValueError) as err:
#        # Any network or JSON issue becomes DataFetchError
        raise DataFetchError(f"Could not fetch user {user_id}") from err


def show_user_profile(user_id: int) -> None:
    try:
        profile = fetch_user_profile(user_id)
    except DataFetchError as err:
        print(err)
#        # maybe show a fallback UI

Real examples of handling multiple exceptions in Python examples at large companies often follow this pattern: they translate many low-level exceptions into a small, app-specific set.


Using try/except/else/finally with multiple exceptions

The else and finally blocks are underused but very handy when you’re juggling several exception types.

Example: Database transaction with cleanup

Imagine a simple database operation using a connection that must always be closed, whether or not exceptions occur.

def save_user(db, user_data: dict) -> None:
    conn = db.connect()
    try:
        cursor = conn.cursor()
        cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)",
                       (user_data["name"], user_data["email"]))
        conn.commit()
    except (KeyError, TypeError) as data_err:
#        # Problems with user_data structure
        conn.rollback()
        print(f"Bad user data: {data_err}")
    except db.IntegrityError as db_err:
#        # Duplicate email, constraint violations, etc.
        conn.rollback()
        print(f"Database integrity error: {db_err}")
    else:
        print("User saved successfully.")
    finally:
        conn.close()

This is one of the best examples of handling multiple exceptions in Python examples around databases: different exception groups trigger the same rollback but different messages, else confirms success, and finally guarantees cleanup.


Pattern: catching, logging, and re-raising

You don’t always want to swallow exceptions. Often, you want to log context and then re-raise so callers can still handle them.

Example: Adding context to multiple exceptions

import logging

logging.basicConfig(level=logging.INFO)


def load_config(path: str) -> dict:
    try:
        with open(path, encoding="utf-8") as f:
            import json
            return json.load(f)
    except (FileNotFoundError, PermissionError, json.JSONDecodeError) as err:
        logging.error("Failed to load config '%s': %s", path, err)
        raise  # Let the caller decide what to do

This pattern shows up a lot in real examples of handling multiple exceptions in Python examples for configuration, environment setup, and CLI tools.


When not to group multiple exceptions

There’s a flip side to all these examples. You can absolutely overdo it.

You should think twice before grouping exceptions when:

  • The recovery behavior is genuinely different
  • You’re hiding important information that callers might need
  • You’re catching very broad exceptions like Exception or BaseException

For instance, this is usually a bad idea:

try:
    risky_operation()
except Exception:  # too broad
    print("Something went wrong.")

In 2024–2025 code reviews, broad except Exception blocks are still one of the top red flags engineers call out. Group exceptions when they are truly related and share a sensible common response.

The official Python style guide, PEP 8, has guidance on exception handling practices at python.org.


FAQ: common questions about multiple exceptions in Python

What are some simple examples of handling multiple exceptions in Python?

A very simple pattern is grouping exceptions into one clause:

try:
    value = int(user_input)
except (ValueError, TypeError):
    print("Invalid number.")

Other straightforward examples include grouping network errors (TimeoutError, connection errors) or file errors (FileNotFoundError, PermissionError) when they share the same fix.

Can I catch multiple exceptions and still know which one happened?

Yes. Catch a tuple and then inspect the exception instance:

try:
    result = risky()
except (ValueError, ZeroDivisionError) as err:
    if isinstance(err, ValueError):
        handle_value_error(err)
    else:
        handle_zero_division(err)

This is a classic example of handling multiple exceptions in Python while preserving type information.

Is it better to use one except with a tuple or many except blocks?

It depends on whether the behavior is the same. If you truly want identical handling and identical messages, a tuple keeps things tidy. If the responses differ, separate except blocks are clearer and easier to maintain.

Should I ever use except Exception for multiple errors?

Only in very controlled places: top-level entry points, logging wrappers, or last-resort error handlers. For everyday code, it’s better to use specific exception types or grouped tuples so you don’t accidentally hide programming bugs.

Where can I learn more and see other examples of error handling patterns?

The official Python tutorial has a solid section on errors and exceptions at docs.python.org. For broader software reliability patterns, many university CS departments, such as MIT OpenCourseWare, publish materials that show error handling in larger systems.


If you keep these patterns in your toolkit—grouping related errors, layering custom exceptions, and using else/finally wisely—you’ll find that most real examples of handling multiple exceptions in Python examples boil down to a few repeatable, readable structures you can reuse across projects.

Explore More Exception Handling

Discover more examples and insights in this category.

View All Exception Handling