> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/unjs/ofetch/llms.txt
> Use this file to discover all available pages before exploring further.

# Auto Retry

> Automatic retry mechanism with configurable retry counts, delays, and status codes

ofetch includes built-in retry logic for failed requests, automatically retrying requests that fail with specific status codes.

## Default Retry Behavior

By default, ofetch retries failed requests once for safe methods (**GET, HEAD, OPTIONS, TRACE**):

```typescript theme={null}
import { ofetch } from 'ofetch'

// Automatically retried once if it fails with a retryable status code
const data = await ofetch('/api/data')
```

Methods that may have side effects (**POST, PUT, PATCH, DELETE**) are **not retried by default** to avoid unintended duplicate operations.

<Note>
  Retries only occur when the response status code matches one of the retryable status codes. Network errors and timeouts are also retried.
</Note>

## Retry Status Codes

The following HTTP status codes trigger automatic retries:

* **408** - Request Timeout
* **409** - Conflict
* **425** - Too Early (Experimental)
* **429** - Too Many Requests
* **500** - Internal Server Error
* **502** - Bad Gateway
* **503** - Service Unavailable
* **504** - Gateway Timeout

These status codes typically indicate temporary issues that may succeed on retry.

## Configuring Retry Count

You can customize the number of retry attempts using the `retry` option:

<ParamField path="retry" type="number | false" default="1 for safe methods, 0 for unsafe methods">
  The number of times to retry the request. Set to `false` to disable retries completely.
</ParamField>

```typescript theme={null}
import { ofetch } from 'ofetch'

// Retry up to 3 times
const data = await ofetch('/api/unreliable', {
  retry: 3
})

// Disable retries
const data = await ofetch('/api/data', {
  retry: false
})

// Enable retries for POST requests
const result = await ofetch('/api/create', {
  method: 'POST',
  body: { name: 'test' },
  retry: 2
})
```

## Retry Delay

You can add a delay between retry attempts:

<ParamField path="retryDelay" type="number | ((context: FetchContext) => number)" default="0">
  Delay in milliseconds between retry attempts. Can be a fixed number or a function that returns the delay based on the fetch context.
</ParamField>

### Fixed Delay

```typescript theme={null}
import { ofetch } from 'ofetch'

// Wait 1 second between retries
const data = await ofetch('/api/data', {
  retry: 3,
  retryDelay: 1000
})
```

### Dynamic Delay (Exponential Backoff)

```typescript theme={null}
import { ofetch } from 'ofetch'

// Exponential backoff: 500ms, 1000ms, 2000ms
const data = await ofetch('/api/data', {
  retry: 3,
  retryDelay: (context) => {
    const retryCount = (context.options.retry as number) || 0
    const maxRetries = 3
    const attempt = maxRetries - retryCount
    return Math.min(1000 * Math.pow(2, attempt), 10000)
  }
})
```

### Retry with Jitter

```typescript theme={null}
import { ofetch } from 'ofetch'

// Add random jitter to prevent thundering herd
const data = await ofetch('/api/data', {
  retry: 3,
  retryDelay: (context) => {
    const baseDelay = 1000
    const jitter = Math.random() * 500
    return baseDelay + jitter
  }
})
```

## Custom Retry Status Codes

You can override the default retry status codes:

<ParamField path="retryStatusCodes" type="number[]" default="[408, 409, 425, 429, 500, 502, 503, 504]">
  Array of HTTP status codes that should trigger a retry.
</ParamField>

```typescript theme={null}
import { ofetch } from 'ofetch'

// Only retry on 429 (Too Many Requests) and 503 (Service Unavailable)
const data = await ofetch('/api/data', {
  retry: 3,
  retryStatusCodes: [429, 503]
})

// Retry on custom status codes
const data = await ofetch('/api/data', {
  retry: 2,
  retryStatusCodes: [408, 500, 502, 503, 504]
})
```

## Retry with Rate Limiting

Handle rate limiting with exponential backoff:

```typescript theme={null}
import { ofetch } from 'ofetch'

const data = await ofetch('/api/rate-limited', {
  retry: 5,
  retryStatusCodes: [429], // Only retry on rate limit
  retryDelay: (context) => {
    // Check for Retry-After header
    const retryAfter = context.response?.headers.get('Retry-After')
    if (retryAfter) {
      const delay = parseInt(retryAfter, 10)
      return isNaN(delay) ? 1000 : delay * 1000
    }
    
    // Fallback to exponential backoff
    const retryCount = (context.options.retry as number) || 0
    const maxRetries = 5
    const attempt = maxRetries - retryCount
    return Math.min(1000 * Math.pow(2, attempt), 30000)
  }
})
```

## Abort Signal and Retries

Abort signals prevent retries from happening:

```typescript theme={null}
import { ofetch } from 'ofetch'

const controller = new AbortController()

// This will NOT retry if aborted
try {
  const data = await ofetch('/api/data', {
    retry: 3,
    signal: controller.signal
  })
} catch (error) {
  console.error(error) // AbortError
}

// Abort the request
controller.abort()
```

<Warning>
  When a request is aborted (via `AbortController` or timeout), ofetch will not retry the request, even if `retry` is configured.
</Warning>

## Retry Logic Flow

1. Request is made
2. If request fails or returns a retryable status code:
   * Check if `retry` count is greater than 0
   * Check if status code is in `retryStatusCodes` (or default list)
   * Check if request was not aborted
3. If all checks pass:
   * Wait for `retryDelay` milliseconds
   * Retry the request with `retry` count decremented by 1
4. If retry count reaches 0, throw `FetchError`

## Complete Example

```typescript theme={null}
import { ofetch } from 'ofetch'

async function fetchWithRetry() {
  try {
    const data = await ofetch('/api/unstable-endpoint', {
      // Retry up to 3 times
      retry: 3,
      
      // Only retry on specific status codes
      retryStatusCodes: [408, 429, 500, 502, 503, 504],
      
      // Exponential backoff with max delay of 10 seconds
      retryDelay: (context) => {
        const retryCount = (context.options.retry as number) || 0
        const maxRetries = 3
        const attempt = maxRetries - retryCount
        const delay = Math.min(1000 * Math.pow(2, attempt), 10000)
        
        console.log(`Retry attempt ${attempt + 1}, waiting ${delay}ms`)
        return delay
      }
    })
    
    return data
  } catch (error) {
    console.error('All retry attempts failed:', error)
    throw error
  }
}
```
