View Source Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
[1.4.0] - 2026-07-31
Added
- Stable external error codes. An error type can declare a
:code(such as"ORDER_NOT_FOUND") that is independent of its Elixir module name, giving external consumers — API clients, i18n catalogs, support tooling — an identifier that survives renaming or moving the module. Retrieve it withErrata.code/1or the generated per-modulecode/1, which is overridable so a type can derive a code from the error's:reasonor:context. Codes are opt-in with no default: a type that does not declare one returnsnil, since deriving a code from the module name would reintroduce the coupling the option exists to break. (#22) - Severity and retryability classification on error types. (#24)
Errata.severity/1returns an error's severity as aLoggerlevel, set per type with the:severityoption. It defaults to:errorfor every kind, so nothing is reclassified unless a type opts in.Errata.retryable?/1returns whether an error is likely transient, set per type with the:retryableoption and defaulting off the error's kind::infrastructureerrors are retryable,:domainand:generalerrors are not. Errata provides no retry mechanism of its own — this is a classification for your own retry logic to branch on.- Both are generated as overridable per-module functions (
severity/1andretryable?/1), following the same pattern ashttp_status/1, so a type can compute either from the error's:reasonor:context. Neither adds a field to the error struct or to theto_map/1/ JSON shape.
Changed
Errata.to_map/1(and therefore the JSON encoding) now includes acodekey, which isnullfor error types that do not declare a:code. This is additive to the serialized shape — the key is always present, consistent with the existingmessage,cause, andenvkeys, which are likewise emitted when empty. Consumers that ignore unknown keys are unaffected. (#22)Errata.log/2now logs at the error'sseverity/1when no level is given, andErrata.report/2withlog: truedoes the same. Since severity is:errorunless a type sets one, this is backward compatible for existing error types. (#24):code,:severity, and:retryableare now included in the metadata emitted byErrata.log/2(as Logger metadata) andErrata.report/2(as top-level[:errata, :error]telemetry metadata), so handlers can route or alert on them. (#22, #24)
Fixed
- The
Errata.error/0type declaredenv: Errata.Env.t(), but an error created withnew/1has no environment. It is nowErrata.Env.t() | nil, matchingErrata.domain_error/0andErrata.infrastructure_error/0and the actual behavior.
[1.3.0] - 2026-06-04
Added
use Errata— a convenience macro for modules that handle or create Errata errors. It imports the three guards (is_error/1,is_domain_error/1,is_infrastructure_error/1) so they can be used unqualified inwhenclauses and function heads, and (becauseimportimpliesrequire) makes theErrata.create/2andErrata.wrap/3macros callable. Only the guards are imported; the rest of the API stays qualified. This is distinct fromuse Errata.Error, which defines a new error type.
[1.2.0] - 2026-06-04
Added
Errata.wrap/2andErrata.wrap/3macros, which wrap a cause in an error of any type while capturing the current__ENV__and stacktrace — the convenience counterpart to the per-modulewrap/2macro, mirroringErrata.create/2. This lets a module wrap causes for several error types without a separaterequirefor each one.
[1.1.0] - 2026-06-03
Added
- Native JSON support: on Elixir 1.18 and later, every error type now implements
the built-in
JSON.Encoderprotocol, soJSON.encode!(error)works with no third-party dependencies. The built-in and Jason backends produce the same JSON shape. (#30)
Changed
jasonis now an optional dependency. Projects that have Jason continue to get a generatedJason.Encoderimplementation exactly as before; projects on Elixir 1.18+ that don't use Jason can now drop it and rely on the built-inJSONencoder. This is backward compatible — anyone who depends onJason.encode!(error)already has Jason in their own dependencies. (#30)
Upgrading
- If your project calls
Jasondirectly but relied on Errata to pull it in transitively, add{:jason, "~> 1.4"}to your own dependencies, since Errata no longer forces it into your dependency tree. On Elixir 1.18+ you can instead use the built-inJSONmodule and drop the Jason dependency entirely.
[1.0.0] - 2026-06-03
First stable release. As of 1.0.0 the public API — the error struct shape, the
Errata guards and helper functions, the generated Errata.Error callbacks, and
the to_map/1 / JSON and [:errata, :error] telemetry shapes — is covered by
Semantic Versioning.
Added
- Context enrichment:
Errata.put_context/3andErrata.merge_context/2add to an error's:contextas it propagates, so intermediate layers can attach context the creation site did not have without rebuilding the struct. (#18) - Declared reasons: error types can now enumerate their valid reasons with the
:reasonsoption (use Errata.DomainError, reasons: [...]). Creating an error with a reason outside the declared set raises anArgumentError(anilreason is always allowed); a:default_reason, if given, must be one of the declared reasons; and areason/0type enumerating them is generated for the docs. (#20) - Error reporting:
Errata.log/2logs an error at a given level with itsreason,kind,context, and origin attached as structured Logger metadata;Errata.report/2emits a[:errata, :error]telemetry event (and optionally logs), providing a vendor-neutral seam for forwarding errors to Sentry, metrics, etc. via a telemetry handler in your application. Adds atelemetry ~> 1.0dependency. (#19) - HTTP status mapping: each error type now has a generated, overridable
http_status/1function (and a matchingErrata.http_status/1) that defaults off the error's kind (:domain→422,:infrastructure→503,:general→500). Set a specific status with the:http_statusoption, or override the function to compute one from the error. No web-framework dependency is added. (#21)
[0.10.0] - 2026-06-02
Added
- Error wrapping (chaining): error types can now carry a
:cause— the original error, exception, or value that led to them — without losing the context of the underlying failure.- A generated
wrap/1,2macro on each error module wraps a caught error as the:causeof a new error, capturing the current__ENV__(likecreate/1) and, when givenstacktrace: __STACKTRACE__, the original error's stacktrace. new/1,create/1, andraise/2now also accept a:causeparam.- The cause is stored as an
Errata.Causestruct (kind/value/stacktrace). Errata.cause/1returns the immediate cause;Errata.root_cause/1walks the chain to the deepest cause;Errata.format_chain/1renders the fullCaused by:chain for logging.to_map/1(and JSON) now include the cause, recursing into wrapped Errata errors and rendering standard exceptions by type and message.
- A generated
[0.9.0] - 2026-06-02
Added
Errata.create/2macro to create an error of any type while capturing the current env, without a separaterequirefor each error module. (#4)Errata.to_map/1to convert any Errata error to a plain, JSON-encodable map without needing to know the error's specific module. (#5)Errata.display_message/1to retrieve the bare, human-readable:messageof an error (without the:reasonsuffix thatException.message/1appends), for rendering errors to end users. (#7)
Changed
- Breaking:
new/1,create/1, andraise/2now raise anArgumentErrorwhen given unrecognized param keys instead of silently ignoring them. Only:message,:reason, and:contextare accepted. Callers that previously relied on extra keys being dropped will need to remove them. (#3)
Fixed
- Serialized error maps (
to_map/1) and their JSON form no longer leak theElixir.prefix on module names:error_typeandenv.moduleare now rendered as e.g."MyApp.Foo"(as strings rather than raw atoms), andenv.file_lineno longer includes a trailing colon. (#6)