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 with Errata.code/1 or the generated per-module code/1, which is overridable so a type can derive a code from the error's :reason or :context. Codes are opt-in with no default: a type that does not declare one returns nil, 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/1 returns an error's severity as a Logger level, set per type with the :severity option. It defaults to :error for every kind, so nothing is reclassified unless a type opts in.
    • Errata.retryable?/1 returns whether an error is likely transient, set per type with the :retryable option and defaulting off the error's kind: :infrastructure errors are retryable, :domain and :general errors 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/1 and retryable?/1), following the same pattern as http_status/1, so a type can compute either from the error's :reason or :context. Neither adds a field to the error struct or to the to_map/1 / JSON shape.

Changed

  • Errata.to_map/1 (and therefore the JSON encoding) now includes a code key, which is null for error types that do not declare a :code. This is additive to the serialized shape — the key is always present, consistent with the existing message, cause, and env keys, which are likewise emitted when empty. Consumers that ignore unknown keys are unaffected. (#22)
  • Errata.log/2 now logs at the error's severity/1 when no level is given, and Errata.report/2 with log: true does the same. Since severity is :error unless a type sets one, this is backward compatible for existing error types. (#24)
  • :code, :severity, and :retryable are now included in the metadata emitted by Errata.log/2 (as Logger metadata) and Errata.report/2 (as top-level [:errata, :error] telemetry metadata), so handlers can route or alert on them. (#22, #24)

Fixed

[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 in when clauses and function heads, and (because import implies require) makes the Errata.create/2 and Errata.wrap/3 macros callable. Only the guards are imported; the rest of the API stays qualified. This is distinct from use Errata.Error, which defines a new error type.

[1.2.0] - 2026-06-04

Added

  • Errata.wrap/2 and Errata.wrap/3 macros, which wrap a cause in an error of any type while capturing the current __ENV__ and stacktrace — the convenience counterpart to the per-module wrap/2 macro, mirroring Errata.create/2. This lets a module wrap causes for several error types without a separate require for 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.Encoder protocol, so JSON.encode!(error) works with no third-party dependencies. The built-in and Jason backends produce the same JSON shape. (#30)

Changed

  • jason is now an optional dependency. Projects that have Jason continue to get a generated Jason.Encoder implementation exactly as before; projects on Elixir 1.18+ that don't use Jason can now drop it and rely on the built-in JSON encoder. This is backward compatible — anyone who depends on Jason.encode!(error) already has Jason in their own dependencies. (#30)

Upgrading

  • If your project calls Jason directly 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-in JSON module 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/3 and Errata.merge_context/2 add to an error's :context as 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 :reasons option (use Errata.DomainError, reasons: [...]). Creating an error with a reason outside the declared set raises an ArgumentError (a nil reason is always allowed); a :default_reason, if given, must be one of the declared reasons; and a reason/0 type enumerating them is generated for the docs. (#20)
  • Error reporting: Errata.log/2 logs an error at a given level with its reason, kind, context, and origin attached as structured Logger metadata; Errata.report/2 emits 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 a telemetry ~> 1.0 dependency. (#19)
  • HTTP status mapping: each error type now has a generated, overridable http_status/1 function (and a matching Errata.http_status/1) that defaults off the error's kind (:domain422, :infrastructure503, :general500). Set a specific status with the :http_status option, 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,2 macro on each error module wraps a caught error as the :cause of a new error, capturing the current __ENV__ (like create/1) and, when given stacktrace: __STACKTRACE__, the original error's stacktrace.
    • new/1, create/1, and raise/2 now also accept a :cause param.
    • The cause is stored as an Errata.Cause struct (kind/value/stacktrace).
    • Errata.cause/1 returns the immediate cause; Errata.root_cause/1 walks the chain to the deepest cause; Errata.format_chain/1 renders the full Caused 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.

[0.9.0] - 2026-06-02

Added

  • Errata.create/2 macro to create an error of any type while capturing the current env, without a separate require for each error module. (#4)
  • Errata.to_map/1 to convert any Errata error to a plain, JSON-encodable map without needing to know the error's specific module. (#5)
  • Errata.display_message/1 to retrieve the bare, human-readable :message of an error (without the :reason suffix that Exception.message/1 appends), for rendering errors to end users. (#7)

Changed

  • Breaking: new/1, create/1, and raise/2 now raise an ArgumentError when given unrecognized param keys instead of silently ignoring them. Only :message, :reason, and :context are 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 the Elixir. prefix on module names: error_type and env.module are now rendered as e.g. "MyApp.Foo" (as strings rather than raw atoms), and env.file_line no longer includes a trailing colon. (#6)