Helpers for constructing structured Temporal failures.
Summary
Functions
Which activity failed, at whatever depth it sits, or nil.
Build an application failure.
Raise an application failure.
Build a cancellation failure.
The wrapped cause, one level down, or nil.
Whether type appears anywhere in the error's cause chain.
True when error is a failure of type, at any of the depths
Temporal nests failures at.
Normalize a failure for encoding: wrap scalar details (and
last_heartbeat_details) into lists and normalize the cause chain.
How a failed activity ended — :non_retryable_failure,
:maximum_attempts_reached, … — or nil when nothing in the chain
carries one. Found at whatever depth it sits, so a child workflow's
wrapper does not hide the activity's retry state.
The failure's Temporal type string — the outermost one in the chain — or
nil.
Every Temporal failure type in the error's cause chain, outermost first.
Functions
Which activity failed, at whatever depth it sits, or nil.
Build an application failure.
:type is the stable string matched by Temporal retry policies.
:retryable? defaults to true and is inverted when encoded to Temporal's
non_retryable wire field.
Raise an application failure.
Build a cancellation failure.
The wrapped cause, one level down, or nil.
Whether type appears anywhere in the error's cause chain.
The unbounded companion to is_failure/2: a function rather than a guard,
so it works at any nesting depth but not in a when clause.
True when error is a failure of type, at any of the depths
Temporal nests failures at.
A guard, so it works in case, with, and function heads:
import Temporalex.Failure, only: [is_failure: 2]
case Payments.charge(amount) do
{:ok, charge} -> ship(charge)
{:error, e} when is_failure(e, "AmountTooLarge") -> refund(e)
{:error, e} -> escalate(e)
endBoth depths are checked because failures arrive in two shapes: a remote
activity's failure is wrapped in a Temporalex.Failure.ActivityError whose
cause carries the type, while a local activity's arrives as the
business error itself. e stays whole either way, so the wrapper's
diagnostics (retry_state, activity_type) remain reachable.
Shapes with no type to compare — a nil cause, an unstructured raise
whose cause is a bare exception, a non-map reason — simply do not match,
rather than raising.
A guard cannot recurse, so this checks three levels: the error, its
cause, and its cause's cause. That covers the shapes Temporal produces —
a remote activity failure (ActivityError → ApplicationError) and a
child workflow wrapping one (WorkflowExecutionError → ActivityError →
ApplicationError). For arbitrary depth — nested child workflows — use
failure?/2, which walks the whole chain but is a function rather than a
guard.
Normalize a failure for encoding: wrap scalar details (and
last_heartbeat_details) into lists and normalize the cause chain.
The NIF's typed encoder requires payload lists; the constructors in this
module already guarantee that, but failures built as struct literals
(raise %ApplicationError{details: %{...}}) may carry scalars. Applied
at the encode boundaries so user code never has to remember.
How a failed activity ended — :non_retryable_failure,
:maximum_attempts_reached, … — or nil when nothing in the chain
carries one. Found at whatever depth it sits, so a child workflow's
wrapper does not hide the activity's retry state.
The failure's Temporal type string — the outermost one in the chain — or
nil.
For the places patterns do not reach — logging, telemetry, error
reporting — so call sites stop hand-writing error.cause.type.
Every Temporal failure type in the error's cause chain, outermost first.
Temporal nests failures: a failed remote activity arrives as an
ActivityError wrapping the business ApplicationError, and a child
workflow wraps that again. This flattens the chain to the types it
carries, so callers do not walk it by hand.