---
title: onRetry
description: Runs each time the SDK schedules a retry for a single-operation call, with the upcoming attempt, its delay, and the triggering error.
---

A constructor [`hook`](/usage#hooks) that runs each time the SDK schedules a retry for a single-operation call, with the upcoming `attempt`, the `delayMs` before it, and the `error` that triggered it. It never fires on the first attempt, for non-retryable errors, or for stream uploads (which aren't retried). The array forms never fire it either — they settle as a single aggregated [`onAction`](/api/onaction) event for the whole call. See [Retries](/retries) for what counts as retryable.

```ts lineNumbers
const files = new Files({
  adapter: s3({ bucket: "uploads" }),
  retries: 3,
  hooks: {
    onRetry(event) {
      logger.warn("files retry", {
        action: event.type,
        key: event.key,
        attempt: `${event.attempt}/${event.maxRetries}`,
        delayMs: event.delayMs,
        code: event.error.code,
      });
    },
  },
});
```

It fires _before_ the wait, so `delayMs` is the [backoff](/retries#backoff) the SDK is about to sleep, not one already spent. `error` is the [`FilesError`](/api/errors) from the attempt that just failed - always a retryable `Provider` failure, since the deterministic codes are never retried.

## Watching for exhaustion

`attempt` counts up from `1`, and `maxRetries` is the ceiling for this call - so `attempt === maxRetries` is the last retry the SDK will schedule. If that one also fails, no further `onRetry` fires and the call settles through [`onError`](/api/onerror) and [`onAction`](/api/onaction). Use the comparison to count calls that burn through their whole retry budget.

```ts lineNumbers
hooks: {
  onRetry(event) {
    metrics.increment("files.retry", { action: event.type });
    if (event.attempt === event.maxRetries) {
      metrics.increment("files.retry.exhausted", { action: event.type });
    }
  },
},
```

A steady stream of retries on one `type` is usually a sign of a throttling or availability problem with that provider - surfacing `event.error.code` alongside the count makes the cause visible.

Like the other hooks, `onRetry` is **fire-and-forget**: it can't change the backoff, cancel the retry, or fail the call - it only observes.

<AutoTypeTable
  path="../../packages/files-sdk/src/index.ts"
  name="FilesRetryEvent"
/>
