# 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.

```ts
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`

```ts
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](/docs/concepts/executor) for the
full outcome contract.

## Further Reading

- [Execution](/docs/concepts/executor)
- [Idempotency](/docs/concepts/idempotency) — how a
  retried `unknown` execution avoids re-running the side effect