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

# RetryableError

> Throw an error with configurable retry behavior

An error that can be retried with configurable retry timing. When thrown from a step function, the step will be retried after the specified delay.

## Constructor

```typescript theme={null}
new RetryableError(message: string, options?: RetryableErrorOptions)
```

## Parameters

<ParamField path="message" type="string" required>
  The error message describing what went wrong.
</ParamField>

<ParamField path="options" type="RetryableErrorOptions" optional>
  Configuration options for the retry behavior.

  <Expandable title="properties">
    <ParamField path="retryAfter" type="number | StringValue | Date" optional>
      The delay before retrying the step.

      * `number`: Milliseconds to wait
      * `StringValue`: Duration string (e.g., "5s", "2m", "1h")
      * `Date`: Specific date/time to retry at

      If not provided, defaults to 1 second (1000 milliseconds).
    </ParamField>
  </Expandable>
</ParamField>

## Properties

<ResponseField name="name" type="string">
  Always set to `"RetryableError"`.
</ResponseField>

<ResponseField name="message" type="string">
  The error message provided to the constructor.
</ResponseField>

<ResponseField name="retryAfter" type="Date">
  The Date when the step should be retried.
</ResponseField>

<ResponseField name="stack" type="string">
  The stack trace where the error was thrown.
</ResponseField>

## Static Methods

<ResponseField name="is" type="(value: unknown) => boolean">
  Type guard to check if a value is a RetryableError.

  ```typescript theme={null}
  if (RetryableError.is(error)) {
    console.log('This error will be retried at:', error.retryAfter);
  }
  ```
</ResponseField>

## Usage

### Basic Retry

Retry with default 1 second delay:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithRetry() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await unstableApiCall();
    } catch (error) {
      // Retry after 1 second (default)
      throw new RetryableError('API call failed');
    }
  });
}
```

### Custom Retry Delay (Duration String)

Specify retry delay as a duration string:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithCustomDelay() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await apiCall();
    } catch (error) {
      throw new RetryableError(
        'API call failed, retrying in 5 seconds',
        { retryAfter: '5s' }
      );
    }
  });
}
```

### Custom Retry Delay (Milliseconds)

Specify retry delay in milliseconds:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithMsDelay() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await apiCall();
    } catch (error) {
      throw new RetryableError(
        'API call failed',
        { retryAfter: 10000 } // 10 seconds
      );
    }
  });
}
```

### Retry at Specific Time

Schedule retry for a specific date/time:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithScheduledRetry() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await apiCall();
    } catch (error) {
      // Retry at 9 AM tomorrow
      const tomorrow = new Date();
      tomorrow.setDate(tomorrow.getDate() + 1);
      tomorrow.setHours(9, 0, 0, 0);
      
      throw new RetryableError(
        'API call failed, retrying tomorrow at 9 AM',
        { retryAfter: tomorrow }
      );
    }
  });
}
```

### Exponential Backoff

Implement exponential backoff using step metadata:

```typescript theme={null}
import { step, RetryableError, getStepMetadata } from 'workflow';

export async function workflowWithExponentialBackoff() {
  "use workflow";

  await step(async () => {
    "use step";
    
    const { attempt } = getStepMetadata();
    
    try {
      await apiCall();
    } catch (error) {
      // Exponential backoff: 2^attempt seconds
      const delay = Math.pow(2, attempt) * 1000;
      
      throw new RetryableError(
        `API call failed (attempt ${attempt})`,
        { retryAfter: delay }
      );
    }
  });
}
```

### Rate Limit Handling

Handle API rate limits:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithRateLimit() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await apiCall();
    } catch (error: any) {
      if (error.status === 429) {
        // API returned Retry-After header
        const retryAfter = error.headers.get('Retry-After');
        
        throw new RetryableError(
          'Rate limited',
          { retryAfter: `${retryAfter}s` }
        );
      }
      throw error;
    }
  });
}
```

### Conditional Retry Logic

Decide retry strategy based on error type:

```typescript theme={null}
import { step, RetryableError, FatalError } from 'workflow';

export async function workflowWithConditionalRetry() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await apiCall();
    } catch (error: any) {
      // Server errors (5xx) - retry with backoff
      if (error.status >= 500) {
        throw new RetryableError(
          'Server error',
          { retryAfter: '10s' }
        );
      }
      
      // Client errors (4xx) - don't retry
      if (error.status >= 400) {
        throw new FatalError('Client error');
      }
      
      // Network errors - retry quickly
      throw new RetryableError(
        'Network error',
        { retryAfter: '2s' }
      );
    }
  });
}
```

### Max Attempts with Retry

Limit retry attempts:

```typescript theme={null}
import { step, RetryableError, FatalError, getStepMetadata } from 'workflow';

export async function workflowWithMaxRetries() {
  "use workflow";

  await step(async () => {
    "use step";
    
    const { attempt } = getStepMetadata();
    const maxAttempts = 5;
    
    try {
      await apiCall();
    } catch (error) {
      if (attempt >= maxAttempts) {
        // Give up after max attempts
        throw new FatalError('Max retry attempts exceeded');
      }
      
      throw new RetryableError(
        `Attempt ${attempt} failed`,
        { retryAfter: '5s' }
      );
    }
  });
}
```

### Type Guard Usage

Check if an error is retryable:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithTypeGuard() {
  "use workflow";

  try {
    await step(async () => {
      "use step";
      throw new RetryableError('Oops', { retryAfter: '5s' });
    });
  } catch (error) {
    if (RetryableError.is(error)) {
      console.log('Will retry at:', error.retryAfter);
    }
  }
}
```

### Long Delays

Retry after extended periods:

```typescript theme={null}
import { step, RetryableError } from 'workflow';

export async function workflowWithLongDelay() {
  "use workflow";

  await step(async () => {
    "use step";
    
    try {
      await checkStatus();
    } catch (error) {
      // Retry after 1 hour
      throw new RetryableError(
        'Status not ready',
        { retryAfter: '1h' }
      );
    }
  });
}
```

## Duration Format

The `retryAfter` option accepts duration strings in the following formats:

* `"1000ms"` - Milliseconds
* `"1s"` - Seconds
* `"1m"` - Minutes
* `"1h"` - Hours
* `"1d"` - Days

## Default Behavior

If `retryAfter` is not specified, the step will be retried after 1 second (1000 milliseconds).

## Notes

* Retryable errors cause the step to be retried after the specified delay
* The step will be re-executed from the beginning on each retry
* Use `getStepMetadata().attempt` to track the current attempt number
* Combine with `FatalError` to handle different error scenarios
* The `retryAfter` date is calculated at the time the error is thrown
* Very long retry delays (hours/days) are supported

## Related

* [FatalError](/api-reference/workflow/fatal-error) - Throw a non-retryable error
* [getStepMetadata](/api-reference/workflow/get-step-metadata) - Get current step attempt number
* [sleep](/api-reference/workflow/sleep) - Pause workflow execution


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.