After a good run of 0.x releases, Errata 1.0.0 is here — the first stable, production-ready release. The public API is now covered by Semantic Versioning, so you can depend on it with confidence.
Errata was first announced here; this thread is the 1.0 milestone, with the full picture of what the library does now.
What is Errata?
Errata is a library for structured, named error handling. In Elixir we usually signal failure either by returning {:error, reason} or by raising an exception — but an ad-hoc reason atom (or worse, a string) carries no context once it’s far from where it was created, and plain exceptions lack a common shape to build logging and reporting around.
Errata replaces both with named error types that share a consistent structure and carry full context about what went wrong and where. The same type works as a raise-able exception and as a value you return in an {:error, _} tuple.
Define an error type in one line
defmodule MyApp.Orders.PaymentDeclined do
use Errata.DomainError,
default_message: "the payment was declined",
reasons: [:insufficient_funds, :fraud_suspected, :card_expired]
end
That generates an exception struct, the Errata.Error behaviour, and String.Chars + Jason.Encoder implementations. Errors come in three kinds — domain, infrastructure, and general — so boundary code can treat business errors differently from system failures.
Every error carries its context
Each Errata error has a well-defined shape:
message— a human-readable descriptionreason— an atom that classifies the error (optionally a declared, validated set)context— arbitrary metadata captured at the site of the errorcause— a lower-level error this one wrapped, preserving the originalenv— the module, function, file, line, and stacktrace where it was created
Because all of that travels with the error, you can create it deep in your code and then log, report, or render it to JSON at a boundary without losing the information needed to interpret it. This pays off especially in with expressions: when each error is a structured type that carries its own context, you can drop the else clause and let errors propagate to a boundary where they’re handled — no loss of detail.
At a boundary, it all comes together
# A Phoenix fallback controller that handles *any* Errata error uniformly:
def call(conn, {:error, error}) when Errata.is_error(error) do
Errata.report(error, log: :warning) # structured Logger metadata + an [:errata, :error] telemetry event
conn
|> put_status(Errata.http_status(error)) # :domain → 422, :infrastructure → 503, :general → 500
|> json(%{error: Errata.display_message(error)}) # the user-facing message, distinct from the dev message
end
A few of the things that round out 1.0:
- Rich creation —
Errata.create/2builds an error of any type while capturing the call site, andwrap/2translates a lower-level failure into one of your own error types without losing the original (rescue e -> PaymentGateway.wrap(e, stacktrace: __STACKTRACE__, reason: :timeout)). - Context enrichment —
put_context/3andmerge_context/2add to an error’s context as it propagates up awithchain, without rebuilding the struct. - Declared reasons — enumerate a type’s valid reasons and have them validated, with a
reason/0type generated into your docs. - Error reporting —
Errata.log/2attaches the error’s fields as structured Logger metadata;Errata.report/2emits a[:errata, :error]telemetry event. It’s a vendor-neutral seam: attach a handler that forwards to Sentry, a metrics backend, or wherever — Errata stays out of the integration business. - HTTP status mapping — an overridable
http_status/1on every error type, defaulting off its kind. - Cause chaining —
cause/1,root_cause/1, andformat_chain/1for following and rendering a chain of wrapped errors. - Classification guards —
is_error/1,is_domain_error/1, andis_infrastructure_error/1for branching at boundaries.
Install
{:errata, "~> 1.0"}
There’s plenty more in the docs — choosing between a distinct error type and a :reason, the domain/infrastructure/general distinction, handling errors with the custom guards, and serialization to JSON.
Thanks to everyone who’s tried Errata and shared feedback along the way; it genuinely shaped the road to 1.0. Feedback and contributions are always welcome.






















