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
- Execution
- Idempotency — how a
retried
unknownexecution avoids re-running the side effect