Skip to content
Zap Studio
retry
Esc
navigateopen⌘Jpreview
On this page

Cancellation

Cancellation through AbortSignal, checked before, between, and during retries.

AbortSignal cancels retries, checked before, between, and during attempts. runRetryPolicy(...) accepts an AbortSignal through the signal option. Use it to stop retrying on user navigation, request timeouts, shutdown, or parent workflow cancellation.

Basic Usage

Pass controller.signal to runRetryPolicy(...); aborting the controller stops the run with an AbortError.

import { AbortError, exponentialBackoff, runRetryPolicy } from "@zap-studio/retry";

const controller = new AbortController();

const policy = exponentialBackoff({
  maxAttempts: 5,
  baseDelayMs: 100,
  maxDelayMs: 2_000,
});

const promise = runRetryPolicy(
  policy,
  async () => {
    return await doWork();
  },
  { signal: controller.signal },
);

controller.abort(new Error("Request canceled by user"));

try {
  await promise;
} catch (error) {
  if (error instanceof AbortError) {
    console.error("Retry aborted:", error.message);
  }
}

Throw Mode

In the default throw mode, an aborted run rejects with AbortError.

import { AbortError, runRetryPolicy } from "@zap-studio/retry";

const controller = new AbortController();

try {
  await runRetryPolicy(
    policy,
    async () => {
      return await doWork();
    },
    { signal: controller.signal },
  );
} catch (error) {
  if (error instanceof AbortError) {
    console.error("Retry aborted:", error.message);
  } else {
    throw error;
  }
}

Non-Throw Mode

With throwOnExhausted: false, an abort resolves to { ok: false } with the AbortError on result.error instead of throwing.

const controller = new AbortController();

const result = await runRetryPolicy(
  policy,
  async () => {
    return await doWork();
  },
  {
    signal: controller.signal,
    throwOnExhausted: false,
  },
);

if (result.ok) {
  console.log(result.value);
} else {
  console.error("Retry stopped:", result.error);
}

Cancellation Timing

The runner checks the signal:

  • before each attempt starts
  • after a failed attempt, before consulting the policy
  • while waiting between retries (the delay is raced against the signal)

This keeps cancellation responsive without custom orchestration code.

Abort Reasons

The abort reason you pass to controller.abort(...) is normalized into the AbortError:

  • an Error reason becomes the AbortError message, with the original error on cause
  • a non-empty string reason becomes the message
  • no reason produces the message "Retry aborted."

See Structured Errors for the full AbortError reference.

Last updated on September 15, 2026

Was this page helpful?