Functional Error Handling in Python: A Guide to the Returns Package
Overview
Python's traditional error handling relies heavily on exceptions and the None value. While standard, this approach often creates brittle code where it's unclear what errors a function might throw. The package introduces functional programming concepts to Python, specifically monadic containers. These containers wrap values to provide a predictable structure for handling missing data, errors, and side effects. By shifting from exceptions to these containers, you can implement Railway-Oriented Programming (ROP), a paradigm where code flows along success or failure tracks without nested try-except blocks.
Prerequisites
To follow this tutorial, you should have a solid grasp of basics, including functions, decorators, and data classes. Familiarity with 's structural pattern matching is highly recommended, as it significantly simplifies working with monadic results.
Key Libraries & Tools
- returns: The primary library for functional error handling, providing
Maybe,Result, andIOcontainers. - Type Annotations: Essential for getting the most out of , though some IDEs like may require specific plugins to handle these complex types.
Code Walkthrough
Handling Missing Data with Maybe
The Maybe container represents a value that might not exist. It has two states: Some (the value exists) and Nothing (the value is missing).

from returns.maybe import Maybe, Some, Nothing
def find_user(user_id: int) -> Maybe[User]:
user = users_db.get(user_id)
return Some(user) if user else Nothing
# Usage with mapping
user_name = find_user(1).map(lambda user: user.name)
In this snippet, find_user doesn't return None. It returns a container. If the user is missing, calling .map() simply does nothing, preventing the dreaded AttributeError: 'NoneType' object has no attribute 'name'.
Railway-Oriented Programming with Result
The Result container handles operations that can fail, using Success and Failure tracks.
from returns.result import Result, Success, Failure
def divide(a: int, b: int) -> Result[int, str]:
if b == 0:
return Failure("Division by zero")
return Success(a // b)
# Chaining operations
result = divide(10, 2).bind(lambda x: Success(x + 10))
By using .bind(), the next function only executes if the previous step was a Success. If any step fails, the Failure state propagates through the chain automatically.
Managing Side Effects with IO
The IO container isolates side effects like file reading or network requests from pure logic.
from returns.io import IOResult, IOSuccess, IOFailure
def read_file(path: str) -> IOResult[str, Exception]:
try:
with open(path, 'r') as f:
return IOSuccess(f.read())
except IOError as e:
return IOFailure(e)
This encapsulates the "impurity" of hitting the disk, forcing the developer to be explicit about how they handle the data once it's retrieved.
Syntax Notes
- The @safe Decorator: Use
@safeto automatically wrap functions that raise exceptions into aResultcontainer. It catches errors and sends them to theFailuretrack. - Structural Pattern Matching: Python's
matchstatement is the cleanest way to extract values from containers:match result: case Success(value): ....
Practical Examples
This approach is ideal for complex data pipelines or SDK development where you want to ensure cross-language compatibility with functional languages like . It's also an excellent educational tool for teaching functional principles without leaving the Python ecosystem.
Tips & Gotchas
Avoid overusing the @safe decorator on every function, as it adds unnecessary boilerplate. Be aware that is a significant departure from standard Pythonic style; it requires full team commitment to remain readable. If you use it, use it everywhere in the project to avoid a confusing mix of exceptions and containers.
- 38%· products
- 13%· products
- 13%· products
- 13%· products
- 13%· products
- 13%· products

Why You Should Think Twice Before Using Returns in Python
WatchArjanCodes // 21:27
On this channel, I post videos about programming and software design to help you take your coding skills to the next level. I'm an entrepreneur and a university lecturer in computer science, with more than 20 years of experience in software development and design. If you're a software developer and you want to improve your development skills, and learn more about programming in general, make sure to subscribe for helpful videos. I post a video here every Friday. If you have any suggestion for a topic you'd like me to cover, just leave a comment on any of my videos and I'll take it under consideration. Thanks for watching!