thiserror
Typed, matchable error variants for Python, inspired by Rust's
thiserror.
Define related errors in one place, attach structured payloads, and give each variant a clear message. Raise the resulting exceptions normally and handle individual variants with Python's match statement.
Features
- Grouped error variants: define named variants in an
Errorsubclass (an enum); calling a variant creates a raisableTypedVariantexception. - Payload schemas: declare one payload type per variant, or use
()for a payload-free (unit) variant. Types are not validated at runtime. - Custom messages: use a function (such as
lambda payload: f"...") or a string template rendered withstr.format. - Pattern matching: distinguish variants with qualified
casepatterns after catching aTypedVariantexception. - Automatic exception causes: when the payload is an exception, it is assigned to the resulting error's
__cause__.
Requires Python 3.12+. No third-party runtime dependencies.
Installation
With uv:
uv add thiserror
With pip:
pip install thiserror
Quick start
from dataclasses import dataclass
from thiserror import Error, TypedVariant, Variant
@dataclass(frozen=True, slots=True)
class InvalidHeaderFields:
expected: str
found: str
class DataStoreError(Error):
Disconnect = Variant(OSError, "data store disconnected")
Redaction = Variant(str, lambda payload: f"the data for key `{payload}` is not available")
InvalidHeader = Variant(
InvalidHeaderFields,
lambda payload: f"invalid header (expected {payload.expected}, found {payload.found})",
)
Unknown = Variant((), "unknown data store error")
fields = InvalidHeaderFields(expected="content-type", found="x-type")
header_error = DataStoreError.InvalidHeader(fields)
print(header_error)
try:
raise DataStoreError.Disconnect(OSError("disk offline"))
except TypedVariant as error:
match error:
case DataStoreError.Disconnect:
print(error)
case DataStoreError.Unknown:
print(error)
Output:
invalid header (expected content-type, found x-type)
data store disconnected
DataStoreError defines the variants; TypedVariant is the exception class. Catch TypedVariant (or Exception), not DataStoreError.
Defining variants
Use Variant(schema, message) as a member of an Error subclass. Variant is an alias for make_variant.
class LookupError(Error):
Missing = Variant(str, "key {0!r} not found")
InvalidStatus = Variant(int, lambda status: f"invalid status: {status}")
Empty = Variant((), "no results available")
assert str(LookupError.Missing("account")) == "key 'account' not found"
assert str(LookupError.InvalidStatus(503)) == "invalid status: 503"
assert str(LookupError.Empty()) == "no results available"
A variant with a payload requires exactly one argument. A unit variant, declared with Variant((), message), takes no arguments. Incorrect argument counts raise TypeError.
Formatting messages
String messages use str.format when the error is converted to a string:
{0}references the entire payload, as in"I/O error: {0}".{name}references a field of a dataclass payload or a value in a payload object's__dict__.- Python format conversions also work, such as
{0!r}forrepr(payload).
Alternatively, pass a callable such as lambda payload: f"invalid: {payload}". This is how the quick-start example uses f-strings: the f-string is evaluated inside the function when the exception is rendered, not when it is created. A plain string passed to Variant uses str.format, not f-string interpolation. For unit variants, a message callable receives the TypedVariant instance rather than a payload.
Accessing the payload
A constructed exception exposes its original payload through .value:
error = LookupError.Missing("account")
assert error.value == "account"
Unknown attributes on the exception are delegated to its payload, so structured payload fields can also be accessed directly (for example, header_error.expected in the quick-start example).
Catching and matching errors
All constructed variants are instances of TypedVariant, which extends Exception. Match against qualified enum members to distinguish variants:
try:
raise LookupError.Empty()
except TypedVariant as error:
match error:
case LookupError.Missing:
print("Missing:", error.value)
case LookupError.Empty:
print("Nothing to return")
case _:
raise
If a payload is itself an exception, it is assigned to __cause__ automatically:
original = OSError("disk offline")
error = DataStoreError.Disconnect(original)
assert error.__cause__ is original
API overview
| Name | Description |
|---|---|
Error |
Base enum for declaring a related set of error variants. It is not an exception type. |
Variant(schema, message) |
Declares a variant with a payload type or () for no payload. |
TypedVariant |
The raisable exception created by calling a declared variant. Its .value contains the payload. |
Limitations
- No runtime payload type validation: schemas provide type information, but the implementation checks the number of arguments, not whether a payload is an instance of the declared type.
- No exhaustiveness guarantees: unlike Rust, Python does not require every variant to appear in a
matchstatement. - No automatic type narrowing by variant: a successful
casedoes not, by itself, ensure static type checkers will infer a more specific payload type.
Metadata
Release files for thiserror 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| thiserror-0.1.2.tar.gz | 6.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| thiserror-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 13.1 kB
Release files / thiserror-0.1.2.tar.gz
| Download URL | thiserror-0.1.2.tar.gz |
|---|---|
| Size | 6.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a7232100184fdc88fe048d197500b03ae7604eed57af28d0a3a74ace3a280919
|
|
BLAKE2b-256 checksum How to use checksums |
1f159edba08e30ab8edf6a977e5d51e215dbce3b29af5cefcc0aa1257b6c02da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / thiserror-0.1.2-py3-none-any.whl
| Download URL | thiserror-0.1.2-py3-none-any.whl |
|---|---|
| Size | 6.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b1e4c9460f1e99c439a0f8932ee5c09dcbfdb6abb087d08108e8c9c440beea21
|
|
BLAKE2b-256 checksum How to use checksums |
3684eeb639c73deba0dfd80b50df4401495c36ec685c45669b47dd7da3d96e81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for thiserror 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for thiserror-0.1.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.