Aviary

API reference

Every public member of WorkflowCtx, the authoring decorators, WorkflowService, the WorkflowEngine's selected surface, DurableModuleOptions, the thrown error classes, and the run/checkpoint status enums — one line each, distilled from source.

This page is a flat lookup table of the public API — the WorkflowCtx, the engine, the errors, the scheduler, and the @dudousxd/nestjs-durable decorators. It is deliberately terse — for the why behind any entry, follow its linked concept/authoring/reliability page.

WorkflowCtx

The context handed to a workflow's run(ctx, input). Every interaction with the outside world goes through it so the engine can checkpoint — the body itself stays deterministic (see Durability & replay).

Properties

MemberSignatureNotes
runIdreadonly runId: stringThis run's id.

Steps

MemberSignatureNotes
stepstep<TIn, TOut>(handler: StepRef<TIn, TOut>, input: TIn, opts?: StepDispatchOpts & { compensate?: StepRef<StepUndo<TIn, TOut>, unknown> | string }): Promise<TOut>Dispatch a durable step by typed @Step method reference — the ONE step primitive, always engine-scheduled. compensate registers a dispatched saga undo for this call. See Workflows & steps.
step (by name)step<TOut = unknown, TIn = unknown>(name: string, input: TIn, opts?: StepDispatchOpts & { compensate?: StepRef<StepUndo<TIn, TOut>, unknown> | string }): Promise<TOut>Same dispatch, by string name — for a cross-runtime handler (e.g. a Python @step) with no JS reference.
localSteplocalStep<TOut>(name: string, fn: (log: StepLogger) => Promise<TOut>, options?: StepOptions): Promise<TOut>Escape hatch: a checkpointed step whose body runs in-process (not dispatched), so it can carry an in-memory compensate for sagas.
transactiontransaction<TOut>(name: string, fn: (tx: unknown) => Promise<TOut>): Promise<TOut>Exactly-once DB step: runs fn and writes the checkpoint in ONE store transaction (business write + "done" marker are atomic). Needs a SQL store; throws otherwise.

Time

MemberSignatureNotes
sleepsleep(duration: string | number): Promise<void>Durable sleep for a relative duration ('30s', '2h', ms) — zero compute, resumes across restarts. See Sleep & signals.
sleepUntilsleepUntil(when: Date | number): Promise<void>Durable sleep to an absolute deadline; the wake time is fixed on first run (replay-stable).
nownow(): Promise<number>Deterministic epoch-ms clock read — records on first run, replays the same value. Use instead of Date.now() inside a workflow body.

Signals & events

MemberSignatureNotes
waitForSignalwaitForSignal<T>(token: string, opts?: { timeoutMs?: number }): Promise<T>Suspend until an external engine.signal(token, payload) arrives; timeoutMs bounds the wait and throws SignalTimeoutError on expiry.
waitForEventwaitForEvent<T>(name: string, opts?: { match?: Record<string, unknown>; timeoutMs?: number }): Promise<T>Suspend for a named pub/sub event published via engine.publishEvent; match filters which publishes resolve this run.
setEventsetEvent<T>(key: string, value: T): Promise<void>Publish a queryable value, readable externally via engine.getEvent(runId, key) while the run is in flight. See Queries & updates.
onUpdateonUpdate<TArg>(name: string, opts?: { timeoutMs?: number }): Promise<TArg>Expose a run-scoped update point; suspends until engine.update(runId, name, arg) delivers arg, gated by any registered validator.

Children

MemberSignatureNotes
childchild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<WorkflowOutputOf<C>>Start (or reattach to) a child run and suspend — zero compute — until it's terminal, then resume with its output (throws FatalError if the child failed). See Child workflows.
child (by name)child<TOut>(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<TOut>Same, for a cross-runtime child by name.
startChildstartChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<string>Fire-and-forget: dispatch a child once and return its run id immediately; the parent keeps running.
allall<C extends WorkflowClass>(workflow: C, inputs: WorkflowInputOf<C>[], opts?: { mode?: 'waitAll' | 'failFast' }): Promise<WorkflowOutputOf<C>[]>Run N children of the SAME workflow in parallel and await all of them, in input order. waitAll (default) aggregates failures into GatherError; failFast throws on the first and cancels the surviving siblings (best-effort, observed at their next checkpoint).

External

MemberSignatureNotes
tasktask<T>(name: string, dispatch: () => Promise<void>, options?: StepOptions): Promise<T>An external task with async completion: run dispatch once, then suspend until engine.completeTask/failTask reports back.
webhookwebhook<TPayload>(): DurableWebhook<TPayload>Mint a durable callback handle (token + optional public url); await handle.wait({ timeoutMs? }) suspends until the third party's callback lands as engine.signal(token, body) — or throws SignalTimeoutError past the deadline. See Durable webhooks.
callEntitycallEntity<TResult = unknown>(name: string, key: string, op: string, arg?: unknown): Promise<TResult>Call a durable entity op (serialized per key) and await its result. See Durable entities.
signalEntitysignalEntity(name: string, key: string, op: string, arg?: unknown): Promise<void>Send an entity op fire-and-forget (no result awaited).

State & metadata

MemberSignatureNotes
patchedpatched(id: string): Promise<boolean>Guard an in-place workflow change without a new version: true for a fresh run (takes the new branch), false for a run whose history predates the guard. See Versioning & determinism.
continueAsNewcontinueAsNew(input?: unknown): Promise<never>End this run and start a fresh execution of the same workflow with clean history (id <runId>~N). Always throws — terminal.
sideEffectsideEffect<T>(fn: () => T | Promise<T>): Promise<T>Deterministic capture: run fn once, checkpoint the result, replay returns the SAME value without re-running fn (e.g. uuidv7(), Math.random()).
upsertSearchAttributesupsertSearchAttributes(attrs: SearchAttributes): Promise<void>Shallow-merge typed, queryable metadata onto this run's searchAttributes — exactly-once (skipped on replay).
breakpointbreakpoint(label?: string): Promise<void>Pause the run here until a human resumes it (engine.continue(runId) or the dashboard) — a durable debugger breakpoint.

Supporting option types

TypeShapeNotes
StepDispatchOpts{ queue?, priority?, fairnessKey?, transport?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?, timeoutMs? }Per-call override of a @Step-declared policy for a dispatched ctx.step. queue routes to a flow-control queue; priority/fairnessKey apply within it. ctx.step's own opts additionally accepts compensate?: StepRef<StepUndo<TIn, TOut>, unknown> | string (not part of StepDispatchOpts itself) — registers a dispatched saga undo for this call, retried per the undo's OWN @Step config (never this call's opts); no queue/admission and no timeoutMs liveness window apply to the undo's dispatch.
StepOptions{ retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?, timeoutMs?, compensate? }Options for ctx.task/ctx.localStep. compensate: () => Promise<void> registers an in-process saga undo, retried up to the module-level compensationRetries (default 1). See Sagas & compensation.
StepUndo<TIn, TOut>{ input: TIn; output: TOut }The one argument a dispatched saga undo is called with — the compensated call's own input and result.
UndoOf<H>H extends (input: infer I, ...rest: never[]) => infer R ? StepUndo<I, Awaited<R>> : neverDerives a dispatched undo's expected argument from the ORIGINAL step method's type, so a compensate ref is compile-checked against the call it undoes: async cancelBooking(undo: UndoOf<FlightService['book']>) { ... }.
ChildCallOptions{ childId?, priority?, version? }Options for ctx.child/ctx.startChild (a bare string is shorthand for { childId }). priority only affects a remote child. version pins the child to that exact registered version — pass a constant, see Versioning.

Decorators

From @dudousxd/nestjs-durable.

@Workflow(options)

Marks a provider class as a durable workflow; its run(ctx, input) becomes the replayed body.

OptionTypeNotes
namestringRequired. Registered workflow name.
versionstringDefault '1'. Old runs keep resuming on the version they started on.
deadLetterWorkflowWorkflowRefRoute this workflow's dead runs to another registered workflow (class or name). Superseded by an inline @DeadLetter() method on the same class. See Dead-letter queue.
tagsstring[]Static labels stamped on every run, merged with per-run start tags.
singletonSingletonConfig ({ key, limit?, maxQueueDepth? })Durable FIFO mutex per key. See Singleton workflows.
executionTimeoutstring | numberMax wall-clock lifetime; an overrunning run is cancelled (execution_timeout) by the timer poller.
inputSchemaclassValidate start input against a class-validator DTO (needs class-validator/class-transformer).
validateInput(input: unknown) => void | Promise<void>Custom validator; takes precedence over inputSchema.
onEventstring[]Start a fresh run whenever any of these events is published. Merged with @OnDurableEvent.
debouncestring | numberCoalesce onEvent triggers: fire once quiet for this long, with the last payload.
batch{ maxSize: number; within: string | number }Coalesce onEvent triggers: fire on size or window, with all payloads.

@Step(nameOrOptions?)

Marks a provider method as a durable step handler, routed BY NAME (derived Class.method, or explicit).

FormNotes
@Step()Bare — name derived as `${ClassName}.${method}`.
@Step('custom:name')Explicit name override.
@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?, timeoutMs? })Object form — input/output are opt-in runtime zod schemas validated at the serve boundary; retries/backoff/backoffMs/backoffMaxMs/jitter/timeoutMs are the def-level dispatch policy ctx.step reads (a per-call opts overrides field-by-field). See Retries & backoff.
StepOptions fieldTypeNotes
namestringExplicit routing name override.
inputz.ZodTypeRuntime input schema.
outputz.ZodTypeRuntime output schema.
retriesnumberMax attempts before the step (and run) fails.
backoff'fixed' | 'exp'Constant or doubling delay between retries.
backoffMsnumberBase delay in ms.
backoffMaxMsnumberUpper bound on the (exponential) delay.
jitterbooleanRandom 50–100% jitter to avoid thundering herds.
timeoutMsnumberLiveness window; no result/heartbeat within it fails the dispatch with RemoteStepTimeout (retryable).

@DeadLetter()

Marks a method on a @Workflow class as its inline dead-letter handler — registered as <workflow>.dlq, receives a DeadLetter payload ({ deadRunId, workflow, input, error? }) when a run of the class is moved to dead. Takes precedence over deadLetterWorkflow. See Dead-letter queue.

@Entity(options) / @On(op)

@Entity({ name }) marks an @Injectable() class as a durable entity (a keyed virtual object, constructible with no arguments); @On(op) marks a method as the handler for operation op, run serialized per key over durable state. Drive with EntityService or ctx.callEntity/signalEntity. See Durable entities.

@OnDurableEvent(...events)

Class decorator subscribing a @Workflow to one or more events — equivalent to @Workflow({ onEvent }); multiple declarations and the option are merged. Named "durable" to avoid clashing with @nestjs/event-emitter's @OnEvent (OnEvent remains a deprecated alias). See Event-driven workflows.

@StepInterceptor()

Marks an @Injectable() class implementing intercept(invocation: StepInvocation, next: () => Promise<unknown>): Promise<unknown> as onion middleware around every LOCAL step's real execution (first-declared runs outermost). Fires only when a step actually executes, never on replay.

WorkflowHandler<TInput, TOutput, A>

Optional, TYPES-ONLY contract (exported from @dudousxd/nestjs-durable-core) for a @Workflow class's run method: implements WorkflowHandler<TInput, TOutput, A> pins the signature against TInput/TOutput/the run's SearchAttributes shape A, so a wrong signature (renamed method, swapped/missing param, wrong return type) is a compile error at the class instead of a runtime discovery failure or a silently wrong type flowing out of ctx.child/engine.start. Nothing reads it at runtime — registration is still via @Workflow + reflected metadata; a class that skips it works exactly as before.

MemberSignatureNotes
runrun(ctx: WorkflowCtx<A>, input: TInput): Promise<TOutput> | TOutputThe pinned workflow body. Pass A (see InferSearchAttributes) to narrow ctx.upsertSearchAttributes inside the body when paired with a @Workflow({ searchAttributes }) schema.

implements WorkflowHandler does not contextually infer run's parameter types the way an object literal assigned to a typed variable would — annotate ctx/input on the method yourself. The interface only checks that what you wrote is assignable; it doesn't write the annotations for you.

DurableWorkflow (class-first statics)

Optional base class — @Workflow classes that extend it start from the class itself, with input/output types inferred from their own run signature. See Class-first starts.

MemberSignatureNotes
startstatic start<C>(this: C, input: WorkflowInputOf<C>, opts?: { id?: string } & StartOptions): Promise<RunResult>Fire-and-forget everywhere: engine.start outside a workflow; ctx.startChild (parent-linked, checkpointed) inside one. opts.id pins the run/child id.
executestatic execute<C>(this: C, input: WorkflowInputOf<C>, opts?: { id?: string; timeoutMs?: number } & StartOptions): Promise<WorkflowOutputOf<C>>Run-and-await everywhere: ctx.child inside a workflow; start + wait-until-terminal outside (throws FatalError if the run didn't complete). timeoutMs bounds the outside wait.

Related helpers: currentWorkflowCtx() returns the ambient WorkflowCtx on the executing body's async path (or undefined outside one); bindWorkflowClass(ctor, client) is what the registrar calls at boot to bind each class to the engine that registered it.

Ambient step logger

Context-local access to the running step's StepLogger, so code deep inside a handler can record events without the logger being threaded through every signature. See Emitting from deep inside a step.

MemberSignatureNotes
currentStepcurrentStep(): StepLogger | undefinedThe logger of the step running on this async path — undefined outside one. Concurrent steps each see their own.
runInStepLoggerrunInStepLogger<T>(logger: StepLogger, fn: () => T): TEngine-internal: binds the SAME logger the step body receives as its second argument.
debug info warn error(message: string, data?: unknown): voidPer-level shortcuts, one per method on StepLogger. The idiomatic form when the level is known at the call site. No-op outside a step.
loglog(level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: unknown): voidSame emission with the level as a value — literal twin of the Python SDK's log(level, …). Reach for it when the level is computed. No-op outside a step.
subsub(name: string, status: 'ok' | 'failed' | 'skipped', message?: string, data?: unknown): voidNo-op outside a step.
subEventsubEvent(e: { id; name; group?; phase?; status?; message?; data? }): voidNo-op outside a step.
subProcesssubProcess<T>(name: string, body: (sp: SubProcessHandle) => Promise<T> | T, opts?: { group?; id? }): Promise<T>Outside a step the body still runs (and still gets a handle); only the emission disappears.

WorkflowService

Injectable NestJS-facing entry point (packages/nestjs/src/workflow.service.ts) — a thin pass-through to the WorkflowEngine it wraps.

MemberSignatureNotes
startstart<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, runId?: string, opts?: StartOptions): Promise<RunResult>Enqueue a run (pending) and return immediately; a worker executes the body. runId defaults to a random id — pass your own for idempotent starts.
resumeresume(runId: string): Promise<RunResult>Resume a run's next turn.
waitForRunwaitForRun(runId: string, opts?: { timeoutMs?: number; until?: 'settled' | 'terminal' }): Promise<RunResult>Resolve once the run settles (terminal or suspended); until: 'terminal' waits past suspensions until completed/failed/cancelled/dead. Pair with start when a request needs the outcome.
signalsignal(token: string, payload: unknown): Promise<RunResult | null>Deliver an external signal to the run waiting on token.
signalWithStartsignalWithStart<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, runId: string, signal: { token: string; payload?: unknown }, opts?: StartOptions): Promise<{ runId: string }>Ensure the run exists, then signal it — race-free (buffered if the run isn't waiting yet). The durable-entity / accumulator pattern.
publishEventpublishEvent(name: string, payload: unknown, opts?: { id?: string }): Promise<number>Resume every run parked on ctx.waitForEvent(name, …) and start every @Workflow({ onEvent }) subscriber; returns the count touched.

WorkflowEngine (selected public surface)

WorkflowService and the decorators cover most app code; reach for the engine directly (injectable) for dashboard/control-plane operations. This is a curated subset, not the full class.

MemberSignatureNotes
startstart(workflow: WorkflowRef, input: unknown, runId: string, opts?: StartOptions): Promise<RunResult>Create + enqueue a run. Idempotent by runId — a redelivered runId returns the existing run's state. opts.version starts that exact registered version instead of the newest (throws if it isn't registered — never falls back).
signalsignal(token: string, payload: unknown): Promise<RunResult | null>Deliver a signal; buffers it if no run is waiting yet (reliable regardless of arrival order).
signalWithStartsignalWithStart(workflow: WorkflowRef, input: unknown, runId: string, signal: { token: string; payload?: unknown }, opts?: StartOptions): Promise<{ runId: string }>Ensure-then-signal, race-free.
updateupdate(runId: string, name: string, arg: unknown): Promise<UpdateResult>Deliver a validated update to a run waiting at ctx.onUpdate(name). Runs the registered validator first; rejection never touches the run.
registerUpdateValidatorregisterUpdateValidator<TArg>(workflow: string, name: string, validate: UpdateValidator<TArg>): voidRegister (or replace) the validator gating update for a (workflow, name) pair. See Queries & updates.
getEventgetEvent<T = unknown>(runId: string, key: string): Promise<T | undefined>Side-effect-free read of the latest value a run published via ctx.setEvent.
publishEventpublishEvent(name: string, payload: unknown, opts?: { id?: string }): Promise<number>Same as WorkflowService.publishEvent.
cancelcancel(runId: string, opts?: { compensate?: boolean }): Promise<RunResult | null>Cancel a run. { compensate: true } resumes it first so registered saga undos run in reverse (status cancelling in the interim) before it settles cancelled.
cancelWherecancelWhere(filter: Omit<RunQuery, 'limit' | 'offset'>, opts?: { compensate?: boolean }): Promise<RunResult[]>Bulk-cancel every run matching a RunQuery filter (workflow/status/tag/search-attribute), each through cancel.
retryWithInputretryWithInput(runId: string, input: unknown, newRunId?: string): Promise<{ runId: string } | null>Fix-and-replay: start a NEW run (default id <runId>~retry~<uuid>) with corrected input, clean history; the original stays inspectable.
deleteRundeleteRun(runId: string): Promise<number>Hard-delete a run and its whole child subtree (cascades depth-first). Returns the count removed. Prefer cancel for a still-live run.
getRunChildrengetRunChildren(parentRunId: string): Promise<string[]>Ids of the runs this run spawned (awaited ctx.child + fire-and-forget ctx.startChild), stable even after they finish.
waitForRunwaitForRun(runId: string, opts?: { timeoutMs?: number }): Promise<RunResult>Same as WorkflowService.waitForRun.
completeTaskcompleteTask(runId: string, name: string, result: unknown): Promise<RunResult | null>Report a ctx.task(name, …)'s async result back to its run.
failTaskfailTask(runId: string, name: string, error: string): Promise<RunResult | null>Report a ctx.task failure — the run resumes and throws a FatalError at the task.
onDeadonDead(listener: (run: WorkflowRun) => void): () => voidSubscribe to dead-lettered runs (moved to dead past maxRecoveryAttempts). Returns an unsubscribe fn.
onEnqueuedonEnqueued(listener: (runId: string) => void): () => voidSubscribe to runs enqueued on ANOTHER instance (control-plane broadcast) — a worker can pick them up immediately instead of waiting for its next poll.
onCancelonCancel(listener: (runId: string) => void): () => voidSubscribe to cancellations on ANY instance — for cooperative in-flight abort.
draindrain(timeoutMs?: number): Promise<void>Graceful shutdown: stop picking up new runs, wait for in-flight executions up to timeoutMs (default 10000).
workerHealthworkerHealth(extra?: string[]): Promise<GroupHealth[]>Per-group queue backlog + live worker heartbeats (only transports that support introspection, e.g. BullMQ).
recoverIncompleterecoverIncomplete(nowMs?: number): Promise<RunResult[]>Resume every run left running by a crash/deploy. Call on boot.
resumeDueTimersresumeDueTimers(nowMs?: number): Promise<RunResult[]>Resume every suspended run whose durable timer is due. Call periodically.
runPendingrunPending(nowMs?: number): Promise<RunResult[]>Pick up and execute every pending run (poll-based dispatch for a broker-less worker pod).
sweepTimeoutssweepTimeouts(now?: number): Promise<void>Cancel in-flight runs that outlived their @Workflow({ executionTimeout }), cascading to their children exactly as cancel does.
continuecontinue(runId: string): Promise<RunResult | null>Resume a run paused at ctx.breakpoint (the dashboard "continue" button). null if not paused there.
getRungetRun(runId: string): Promise<WorkflowRun | null>Read a run's current persisted state.
registerEntityregisterEntity<S>(name: string, config: EntityConfig<S>): voidRegister a durable entity's handlers + initial state.
useuse(interceptor: StepInterceptor): () => voidRegister a StepInterceptor (onion middleware around real local-step execution).
subscribesubscribe(listener: EngineListener): () => voidSubscribe to every EngineEvent (lifecycle events) the engine emits.

engine.runSchedules is not a method — runSchedules(engine, schedules, nowMs, opts?) is a standalone function exported from @dudousxd/nestjs-durable-core (scheduler.ts) that starts each ScheduledWorkflow's current time-bucket run id (idempotent via engine.start). The NestJS module's schedules option wires it to the timer poller automatically — see Scheduling.

DurableModuleOptions

Options for DurableModule.forRoot / forRootAsync. The role (operator / thin worker / operator + co-located worker) is inferred from which of store / connection are set.

OptionTypeDefaultNotes
storeStateStoreSet to play the operator role. See State stores.
transportTransportSingle task transport. Required alongside store. See Transports.
transportsNamedTransport[]Ordered pool for failover / multi-broker, instead of transport.
controlPlaneControlPlanefirst broadcast-capable transportCross-instance lifecycle-event + cancellation pub/sub.
timerPollMsnumber1000Durable-timer poller interval; 0 disables it. Operator only.
autoSchemabooleantrueAuto-create durable tables via store.ensureSchema() on boot. Turn off in production; migrate instead.
namespacestringunset = operatorWorker-pool partition; poll paths only act on runs in this namespace. See Tenancy.
leaseMsnumber30000Multi-instance recovery-lease duration. Operator only.
instanceIdstringrandomUnique id for this instance (leases, worker heartbeats).
maxRecoveryAttemptsnumberunlimitedCap recovery attempts before a poison-pill run moves to dead. See Dead-letter queue.
remoteAdvanceSilenceMsnumberunbounded awaitLiveness deadline for a remote (polyglot) workflow advance; each heartbeat rearms it. Operator only.
deadLetterWorkflowWorkflowRefnoneDefault DLQ target for workflows without their own. Operator only.
drivebooleantrueWhether this operator instance actively drives runs (poll/recover/resume/prune/consume). false = dashboard/dispatch-only.
shutdownTimeoutMsnumber10000Max ms to wait for in-flight runs on shutdown. Operator only.
schedulesScheduledWorkflow[]noneRecurring workflows (fixed interval or cron), fired by the timer poller on driving instances. See Scheduling.
retentionDurableRetentionOptions ({ policies, sweepInterval?, batchSize? })keep all historyHard-prune terminal run history on an interval. See Run retention & pruning.
webhookUrl(token: string) => stringnone (build your own)Builds DurableWebhook.url for ctx.webhook(). Operator only.
queuesQueueConfig[]noneFlow-control queues registered on the engine at startup. See Flow control.
admissionAdmissionBackendin-process capsBackend for queues — pass a Redis-backed one to make limits fleet-global. Operator only.
traceparent() => string | undefinednoneCurrent W3C traceparent to stamp on dispatched remote tasks. Operator only.
context() => Record<string, unknown> | undefinedauto-fed from @dudousxd/nestjs-context if installed, else noneOpaque context carrier (tenant/user/correlation ids) stamped on dispatched tasks. Operator only.
compensationRetriesnumber1Attempts per IN-PROCESS (ctx.localStep) saga compensation on run failure. A dispatched (ctx.step) compensation retries per its own @Step config instead. Operator only.
scopeReadsbooleanfalseConfine the store's reads to namespace (tenant-boundary view) instead of the operator (all-namespaces) view. Needs a store with withScope.
connectionstring | Record<string, unknown>ioredis connection for a thin worker or a co-located worker consumer. Set to play a worker role.
partitionstring'default'Isolation partition a worker role serves/dispatch-suffixes (<name>@<partition>). Ignored for a plain operator.
prefixstring'durable'Key prefix namespacing the durable queues for a worker role's consumer. Ignored for a plain operator.
concurrencyConcurrencyOption (number | 'adaptive' | { mode: 'adaptive', ... })1Tasks a worker role's consumer runs concurrently per subscribed queue. Ignored for a plain operator.
concurrencyByHandlerRecord<string, ConcurrencyOption>falls back to concurrencyPer-handler concurrency override — not yet wired through runRedisWorker.
runGatewayTimeoutMsnumber10000Timeout for a thin worker's RunGateway round-trip over transport. Ignored for an operator.
topology{ role: 'control-plane' } | { role: 'tenant'; tenant: string }none = existing inferenceExplicit, VALIDATED role preset — see Roles & config. 'tenant' maps tenant onto partition for you.

Errors

All exported from @dudousxd/nestjs-durable-core.

ErrorThrown when
FatalErrorThrown BY YOU inside a step/workflow for an unrecoverable business failure (e.g. a declined card) — the engine never retries it and fails the run immediately.
SignalTimeoutErrorctx.waitForSignal / ctx.waitForEvent / ctx.onUpdate's timeoutMs deadline passes before delivery. Catchable in the workflow to take a default branch.
NonDeterminismErrorOn resume, the step at a logical position has a different name/kind than its checkpoint — the workflow code changed under an in-flight run without a new @Workflow version. See Versioning & determinism.
SingletonQueueFullErrorstart on a singleton workflow whose in-flight + gated backlog already equals limit + maxQueueDepth. Back-pressure — retry later. See Singleton workflows.
GatherErrorctx.all — one or more parallel children failed; carries per-item { index, id, error } failures.
RemoteStepTimeoutA dispatched step produces no result/heartbeat within its timeoutMs — the worker is presumed dead. Retryable, subject to the step's retries.
RemoteWorkflowTimeoutA remote WorkflowExecutor.advance produces no decision/heartbeat within its configured timeoutMs. Recoverable, not a run failure — the engine releases the lease and lets recoverIncomplete re-drive.
ContinueAsNewThrown internally by ctx.continueAsNew(input) to unwind the current run and hand off to a fresh execution. A control-flow signal — a workflow's catch must rethrow it untouched.
WorkflowSuspendedInternal control signal the engine throws to stop execution and persist wakeAt/suspension state. A control-flow signal — never throw or catch-and-handle it yourself; rethrow untouched.

Control-flow signals

WorkflowSuspended/ContinueAsNew above (and @dudousxd/durable-worker's Suspend, the thin worker's equivalent) are thrown internally to unwind the current turn — a durable sleep, a still-pending step, ctx.continueAsNew — never real failures. Because they're thrown with the same JS exception mechanism a real step failure uses, a workflow's own try/catch around a ctx.step/ctx.sleep/etc. can observe one — and MUST rethrow it untouched before running any cleanup/compensation, or the cleanup gets recorded as an extra history command during what replay will later see as a plain suspend, corrupting the run (NonDeterminismError on resume). See Sagas: the control-flow-signal pitfall and Troubleshooting.

ExportSignatureNotes
CONTROL_FLOW_SIGNALconst CONTROL_FLOW_SIGNAL: unique symbolSymbol.for('aviary:durable:control-flow') — the shared, global-registry marker stamped on every control-flow signal class, across package/module-instance boundaries. Rarely used directly; prefer isWorkflowControlFlowSignal.
isWorkflowControlFlowSignalisWorkflowControlFlowSignal(error: unknown): booleantrue for WorkflowSuspended, ContinueAsNew, or the thin worker's Suspend — regardless of which runtime threw it. Not true for Cancelled (a terminal, legitimately-catchable outcome) or a StepFailed/ordinary step rejection (a real failure a saga's catch is meant to compensate for).

Both are exported from @dudousxd/nestjs-durable-core and re-exported by @dudousxd/nestjs-durable.

Statuses

Run status (RunStatus)

StatusMeaning
pendingCreated + enqueued by start, not yet picked up.
runningA worker is actively executing (or replaying) this run's body.
suspendedParked with zero compute — on a sleep, a signal/event/update, an awaited step or child.
cancellingA compensating cancel({ compensate: true }) is in progress (saga undo running). Non-terminal.
completedFinished successfully. Terminal.
failedFinished with an unrecovered error. Terminal.
cancelledCancelled (with or without compensation). Terminal.
deadDead-lettered: crash-recovery gave up after maxRecoveryAttempts (a poison pill). Terminal.

Only completed / failed / cancelled / dead are terminal — eligible for retention pruning and never transition on their own again.

Checkpoint status (StepCheckpoint['status'])

StatusMeaning
pendingA remote step dispatched and awaiting its worker's result; the run is durably suspended.
runningA local step's body is executing in-process right now.
completedSettled successfully — short-circuits on replay (the step is NOT re-executed).
failedSettled with an error — re-awaited (remote) or re-run (local) on replay unless retries are exhausted.

On this page