Timeout

Native AbortSignal is used for cooperative cancellation; a timeout means Kaji stopped waiting, not that a side effect was rolled back.

Kaji uses the platform’s native AbortSignal instead of a Kaji-specific cancellation token. A capability’s execute function receives the effective signal on its ExecutionContext and must check it cooperatively wherever the underlying operation supports cancellation.

execute: async (input, context) => {
  return refundPayment({
    ...input,
    idempotencyKey: context.idempotencyKey,
    signal: context.signal,
  });
},

Checkpoints

The executor checks the effective signal before every step that hasn’t yet started a side effect: before claiming the idempotency key, before authorization, and before approval. If the signal is already aborted at any of those points, the request settles cancelled and execute never runs.

Once execute has been called, cancellation can no longer prevent the action — it can only ask the capability to stop cooperating. A signal that aborts while execute is running does not imply the side effect was rolled back or didn’t happen; Kaji cannot force hostile or uncooperative application code to stop, and a remote system can complete its work after Kaji stops waiting.

timeoutMs

const kaji = createKaji({
  store: memoryStore(),
  timeoutMs: 30_000,
});

timeoutMs combines with any caller-supplied signal into one effective signal using AbortSignal.any() — both are native primitives, with no Kaji-specific equivalent layered on top. When the timeout elapses, Kaji aborts that signal exactly the way it would abort on caller-initiated cancellation.

A timeout means Kaji stopped waiting — nothing more. It is not proof that a remote side effect didn’t happen, and Kaji never automatically retries a timed-out execution as though it never started.

Why Unknown

If execute has already been called when the effective signal aborts, the resulting failure settles unknown, not cancelled or an ordinary failed. Kaji cannot distinguish “the side effect never started” from “the side effect committed and the acknowledgement was lost” once the boundary into application code has been crossed — so it preserves that ambiguity instead of guessing. See Execution for the full outcome contract.

Further Reading