---
title: Structured Errors
description: "RetryError on exhaustion, AbortError on cancellation — both structured terminal errors."
type: package
package: "@zap-studio/retry"
---

`retry` throws two structured terminal errors: `RetryError` on exhaustion and `AbortError` on cancellation. Both extend `Error`.

## Import

```ts
import { AbortError, RetryError } from "@zap-studio/retry";
```

## RetryError

### Constructor

```ts
new RetryError(message: string, context: RetryErrorContext)
```

| Option              | Type      | Default | Description                                          |
| ------------------- | --------- | ------- | ---------------------------------------------------- |
| `context.attempts`  | `number`  | —       | Count of completed attempts at exhaustion. Required. |
| `context.lastError` | `unknown` | —       | Last error raised by a failed `execute` attempt.     |
| `context.lastData`  | `unknown` | —       | Optional data captured from the last attempt.        |

### Properties

| Property    | Type      | Description                     |
| ----------- | --------- | ------------------------------- |
| `name`      | `string`  | Always `"RetryError"`           |
| `message`   | `string`  | Human-readable terminal message |
| `attempts`  | `number`  | Total attempts performed        |
| `lastError` | `unknown` | Last captured error             |
| `lastData`  | `unknown` | Last captured data value        |

## AbortError

### Constructor

```ts
new AbortError(message: string, context?: AbortErrorContext)
```

| Option          | Type      | Default | Description                                         |
| --------------- | --------- | ------- | --------------------------------------------------- |
| `context.cause` | `unknown` | —       | Wrapped cause when the abort reason was an `Error`. |

### Properties

| Property  | Type      | Description                                                    |
| --------- | --------- | -------------------------------------------------------------- |
| `name`    | `string`  | Always `"AbortError"`                                          |
| `message` | `string`  | Human-readable abort message                                   |
| `cause`   | `unknown` | Original abort reason when it was an `Error`, else `undefined` |

## Default Behavior

The default `onExhausted` used by `runRetryPolicy(...)` (when a policy omits its own) returns a `RetryError` with the message `"Retry policy exhausted all attempts."` — the built-in `fixedDelay`, `linearBackoff`, and `exponentialBackoff` policies rely on this default. When retries are exhausted in the default throw mode, `runRetryPolicy(...)` throws that error.

## Catching Terminal Errors

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

const policy = exponentialBackoff({
  maxAttempts: 3,
  baseDelayMs: 100,
  maxDelayMs: 500,
});

try {
  await runRetryPolicy(policy, async () => {
    return await doWork();
  });
} catch (error) {
  if (error instanceof RetryError) {
    console.error("Attempts:", error.attempts);
    console.error("Last error:", error.lastError);
  } else if (error instanceof AbortError) {
    console.error("Aborted:", error.message);
  } else {
    throw error;
  }
}
```

See [Non-throw Mode](/retry/non-throw-mode) for how these same errors surface as a `result.error` instead of being thrown.

## Custom Terminal Errors

Supply `onExhausted(...)` in a [custom policy](/retry/custom-policies) to return your own `RetryError` subclass while keeping the same retry orchestration flow.
