Temporalex.Failure (Temporalex v0.5.4)

Copy Markdown View Source

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

activity_type(error)

Which activity failed, at whatever depth it sits, or nil.

application(message, opts \\ [])

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.

application!(message, opts \\ [])

@spec application!(
  term(),
  keyword()
) :: no_return()

Raise an application failure.

cancelled(message \\ "cancelled", opts \\ [])

Build a cancellation failure.

cause(arg1)

The wrapped cause, one level down, or nil.

failure?(error, type)

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.

is_failure(error, type)

(macro)

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)
end

Both 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 (ActivityErrorApplicationError) and a child workflow wrapping one (WorkflowExecutionErrorActivityErrorApplicationError). For arbitrary depth — nested child workflows — use failure?/2, which walks the whole chain but is a function rather than a guard.

normalize(failure)

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.

retry_state(error)

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.

type(error)

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.

types(error)

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.