Practical examples of using assertions for error handling in Python
Quick, realistic examples of using assertions for error handling in Python
Let’s start with a few small but realistic examples of using assertions for error handling in Python, then we’ll unpack the patterns.
1. Guarding function assumptions in data processing
You’re normalizing user ages from a CSV file. Ages should be integers between 0 and 120. Instead of silently accepting garbage, you assert your assumptions:
def normalize_age(age):
# # Developer-facing safety check, not user-facing validation
assert isinstance(age, int), "age must be int"
assert 0 <= age <= 120, "age must be between 0 and 120"
return age
``
Here the assertion is not user input validation. It’s a sanity check for developers: if upstream code passes a string or a ridiculous age, something is wrong with the pipeline wiring. In logs or tests, this is a loud signal that the contract was broken.
### 2. Catching impossible states in business logic
Suppose you have a subscription system where a user can be either `trial`, `active`, or `canceled`. If you ever see anything else, that’s a bug, not a user error:
```python
VALID_STATUSES = {"trial", "active", "canceled"}
def process_subscription(status):
# # This should never be violated if the database and code are correct
assert status in VALID_STATUSES, f"Unexpected subscription status: {status!r}"
if status == "trial":
... # send nudges
elif status == "active":
... # provide full access
elif status == "canceled":
... # disable access
These kinds of examples of using assertions for error handling in Python highlight the core idea: assertions document and enforce internal invariants, not user-facing rules.
3. Verifying algorithm preconditions
Imagine a function that expects a sorted list because it uses binary search internally:
def find_user_id(sorted_ids, target):
# # Developer contract: sorted_ids must be sorted ascending
assert sorted_ids == sorted(sorted_ids), "sorted_ids must be sorted"
# # binary search implementation...
If someone later refactors call sites and forgets to sort, this assertion fires quickly during development or testing instead of causing subtle, intermittent bugs.
Why assertions are not general-purpose error handling
Before going deeper into more examples of using assertions for error handling in Python, you need one hard rule burned into your brain:
Python removes assertions entirely when you run with optimization flags like
python -O.
That means:
assert user_is_logged_in, "User must be logged in"
can become a no-op in production if someone enables optimization. No exception. No message. Nothing.
So assertions are a tool for developer diagnostics, not for normal runtime error handling like:
- Validating user input
- Handling network failures
- Enforcing security constraints
- Checking file permissions or external resources
For those, you should raise explicit exceptions:
if not user_is_logged_in:
raise PermissionError("User must be logged in")
Assertions are best used as “tripwires” for bugs: internal conditions that should never be violated if the code is correct.
For the official stance, the Python docs on assert are worth reading: https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement.
Best examples of using assertions for error handling in Python internals
Let’s look at some of the best examples of using assertions for error handling in Python when you’re dealing with internal logic instead of user-facing errors.
4. Enforcing invariants in a financial transaction object
Financial calculations are notoriously sensitive to tiny mistakes. You can use assertions to enforce invariants that must always hold if the code is correct.
from decimal import Decimal
class Transaction:
def __init__(self, amount, fee):
self.amount = Decimal(amount)
self.fee = Decimal(fee)
# # Invariant: amounts cannot be negative
assert self.amount >= 0, "Transaction amount cannot be negative"
assert self.fee >= 0, "Transaction fee cannot be negative"
def net_amount(self):
net = self.amount - self.fee
# # Another invariant: net amount should not be negative
assert net >= 0, "Net transaction amount cannot be negative"
return net
If a refactor accidentally flips a sign or misapplies a fee, these assertions fail early in tests, instead of producing quiet accounting errors downstream.
5. Validating internal state transitions in a state machine
State machines are a classic place where examples of using assertions for error handling in Python shine. You can make illegal transitions impossible to ignore.
class Order:
STATES = {"created", "paid", "shipped", "delivered"}
def __init__(self):
self.state = "created"
def mark_paid(self):
assert self.state == "created", "Order must be 'created' before 'paid'"
self.state = "paid"
def ship(self):
assert self.state == "paid", "Order must be 'paid' before 'shipped'"
self.state = "shipped"
def deliver(self):
assert self.state == "shipped", "Order must be 'shipped' before 'delivered'"
self.state = "delivered"
Here you’re not validating user input. You’re catching bugs in the orchestration logic. If some background job tries to ship an order that was never paid, the assertion fires immediately during QA.
Examples of using assertions for error handling in Python data pipelines
Modern Python codebases often revolve around data engineering and machine learning. In that world, silent data drift can be more dangerous than explicit crashes. Assertions can act as circuit breakers.
6. Checking schema assumptions in ETL code
Suppose you’re reading data from an external API into a pandas DataFrame. You expect certain columns to be present.
import pandas as pd
EXPECTED_COLUMNS = {"user_id", "country", "age"}
def validate_schema(df: pd.DataFrame) -> pd.DataFrame:
missing = EXPECTED_COLUMNS - set(df.columns)
assert not missing, f"Missing required columns: {missing}"
# # Optional: ensure no duplicate user_id values in this batch
assert df["user_id"].is_unique, "user_id must be unique per batch"
return df
In production, you’d typically pair this with logging and explicit exceptions, but during development and testing, these assertions catch upstream API changes before they corrupt downstream analytics.
7. Sanity-checking model inputs in machine learning
You train a classification model on features scaled to [0, 1]. Months later, a new data source sneaks in values outside that range. You can use assertions to catch this in your preprocessing code.
import numpy as np
def preprocess_features(X: np.ndarray) -> np.ndarray:
# # Developer sanity checks
assert X.ndim == 2, "Features must be a 2D array"
assert np.all(X >= 0) and np.all(X <= 1), "Features must be in [0, 1]"
return X.astype("float32")
In 2024, as ML pipelines get more automated and data sources more chaotic, these small assertions often surface issues earlier than model metrics. They’re not a replacement for monitoring, but they’re a cheap backstop.
For more on data quality and validation patterns (outside of Python specifically), it’s worth looking at general data integrity guidance from organizations like NIST: https://www.nist.gov/itl.
When assertions are a bad idea for error handling
Now for the uncomfortable part. There are also very clear anti-patterns. Some of the worst examples of using assertions for error handling in Python come from mixing up user-facing errors and developer-facing bugs.
8. Don’t use assertions for user input validation
This is a classic mistake:
## Bad pattern
username = input("Username: ")
assert username, "Username is required"
If Python is run with -O, that assertion disappears and empty usernames slip through. Instead:
username = input("Username: ")
if not username:
raise ValueError("Username is required")
Assertions should never be your only line of defense for anything that depends on untrusted input: HTTP requests, CLI arguments, file uploads, API payloads, etc.
9. Don’t use assertions for security checks
An even scarier version:
## Very bad pattern
assert current_user.is_admin, "Admin access required"
This is a security vulnerability waiting to happen. Run with optimization, and your access control vanishes. Always use explicit checks and raise appropriate exceptions (or return HTTP 403, etc.).
For general security guidance, the OWASP foundation has good language-agnostic advice: https://owasp.org/.
How assertions interact with testing and tooling in 2024
One reason examples of using assertions for error handling in Python remain popular in 2024–2025 is how well they integrate with modern testing tools.
Assertions and pytest
pytest rewrites plain assert statements in tests to provide rich failure messages. For example:
def test_net_amount_positive():
tx = Transaction(amount="10.00", fee="2.00")
assert tx.net_amount() == 8
When this fails, pytest shows a detailed diff, not just AssertionError. This pattern is test-only, but it reinforces the mental model: assertions are about developer feedback, not user experience.
Assertions in type-checked code
If you use mypy or other static type checkers, assertions can help narrow types at runtime:
from typing import Optional
def get_user_name(user: Optional[dict]) -> str:
assert user is not None, "user must not be None here"
# # After this assert, type checkers treat `user` as non-optional
return user["name"]
Here, the assertion both documents an assumption and helps the type checker understand your intent.
Patterns for safe, maintainable use of assertions
Let’s pull the thread together and summarize patterns that produce the best examples of using assertions for error handling in Python without shooting yourself in the foot.
Use assertions for:
- Internal invariants: “If this is false, the code is broken.”
- Algorithm assumptions: sortedness, non-empty collections, dimensions.
- State machine transitions: illegal transitions indicate a bug.
- Data pipeline sanity checks: schema, ranges, shapes, but backed by logging and monitoring.
- Developer contracts: documenting expectations between internal functions.
Avoid assertions for:
- Anything user-controlled: form fields, API input, CLI args, HTTP headers.
- Security, auth, or permission checks.
- Handling I/O errors, network failures, or external services.
- Business rule violations that need clear user messages.
In other words, assertions are your debugging ally, not your public API’s bouncer.
FAQ: common questions and examples
Can you give another simple example of using assertions for error handling in Python?
Here’s a small, focused example in a configuration loader:
import json
def load_config(path):
with open(path) as f:
cfg = json.load(f)
# # Internal guarantees for the rest of the app
assert "database" in cfg, "Config must contain 'database' section"
assert "host" in cfg["database"], "Database config must include 'host'"
return cfg
If someone edits the config file incorrectly, this fails fast during startup in development or staging, instead of producing obscure errors later.
Are assertions faster than regular if + raise?
In CPython, an assert is roughly equivalent to:
if __debug__:
if not condition:
raise AssertionError(message)
So performance differences are usually negligible. The real difference is that assertions can be stripped entirely when __debug__ is False (with python -O). That’s why using them as your main error handling mechanism is dangerous.
How many assertions are too many?
If every second line in your code is an assertion, you probably have a design problem. A good rule of thumb: use them where a failure would indicate a logic bug, not an expected runtime condition. If you find yourself handling assertion failures in normal control flow, you’re misusing them.
Should I log assertion failures?
In most cases, you let the exception bubble up and crash loudly in development or testing. In production, if you don’t run with -O, you might want a top-level exception handler that logs AssertionError with full stack traces. That said, if assertions are firing in production, you likely have a bug that needs immediate attention.
Where can I learn more about Python error handling patterns?
Besides the official Python docs, many university CS courses and open materials cover defensive programming and invariant checks. For example, MIT’s open courseware on software construction concepts (hosted at https://ocw.mit.edu/) often touches on invariants and assertions, even if not Python-specific.
Assertions are one of those features that are easy to abuse and easy to underestimate. Used thoughtfully, they give you sharp, targeted feedback about broken assumptions. Used as a shortcut for real error handling, they quietly disappear and leave you with brittle, confusing behavior. The best examples of using assertions for error handling in Python all share the same trait: if the assertion fails, it means the code is wrong, not the user.
Related Topics
Practical examples of try-catch block example in Java for real-world debugging
Practical examples of using assertions for error handling in Python
Practical examples of handling multiple exceptions in Python
Examples of Raising Exceptions in Python: 3 Practical Patterns You’ll Actually Use
Explore More Exception Handling
Discover more examples and insights in this category.
View All Exception Handling