> ## 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.

# Error Reference

> Comprehensive guide to error types and handling in Workflow DevKit.

This reference documents all error types in Workflow DevKit, covering both workflow-level errors and runtime errors. Understanding these errors helps you build robust, production-ready workflows.

## Error Categories

Workflow DevKit errors fall into two main categories:

### Workflow Errors

Errors you can throw and handle within your workflow code to control execution flow:

* **FatalError** - Stop retries and fail the step immediately
* **RetryableError** - Customize retry timing and behavior

### Runtime Errors

Errors thrown by the Workflow DevKit when internal operations fail:

* **WorkflowError** - Base class for all Workflow DevKit errors
* **WorkflowAPIError** - HTTP request failures
* **WorkflowRunFailedError** - Workflow execution failures
* **WorkflowRunNotCompletedError** - Accessing incomplete run results
* **WorkflowRuntimeError** - Internal runtime issues
* **WorkflowRunNotFoundError** - Missing workflow runs
* **WorkflowRunCancelledError** - Cancelled workflow runs
* **RunNotSupportedError** - Version incompatibility

## Quick Start

### Handling API Errors

```typescript theme={null}
import { WorkflowAPIError } from 'workflow';

try {
  await startWorkflow('myWorkflow', input);
} catch (error) {
  if (error instanceof WorkflowAPIError) {
    console.error(`API error (${error.status}):`, error.message);
    if (error.retryAfter) {
      console.log(`Retry after ${error.retryAfter} seconds`);
    }
  }
}
```

### Using FatalError in Steps

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

async function validateUser(userId: string) {
  "use step";
  
  const user = await fetchUser(userId);
  
  if (!user) {
    // Don't retry - user doesn't exist
    throw new FatalError('User not found');
  }
  
  return user;
}
```

### Customizing Retry Timing

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

async function callRateLimitedAPI(endpoint: string) {
  "use step";
  
  const response = await fetch(endpoint);
  
  if (response.status === 429) {
    // Retry after 1 minute
    throw new RetryableError('Rate limited', {
      retryAfter: '1m'
    });
  }
  
  return response.json();
}
```

## Error Documentation Pages

<Accordion title="Workflow Errors">
  ### [FatalError](/errors/workflow-errors#fatalerror)

  Thrown to indicate an unrecoverable error that should not be retried. Use this when a step encounters a permanent failure condition like invalid input or missing resources.

  **When to use:**

  * Resource not found (404)
  * Invalid input validation
  * Permission denied (403)
  * Business logic violations

  ### [RetryableError](/errors/workflow-errors#retryableerror)

  Thrown to customize retry behavior with specific timing. Use this when you know how long to wait before retrying (e.g., rate limits, backpressure).

  **When to use:**

  * Rate limiting (429)
  * Temporary service unavailability
  * Exponential backoff scenarios
  * Custom retry delays
</Accordion>

<Accordion title="Runtime Errors">
  ### [WorkflowError](/errors/runtime-errors#workflowerror)

  Base class for all Workflow DevKit errors. Use with `instanceof` to catch any Workflow-related error.

  ### [WorkflowAPIError](/errors/runtime-errors#workflowapierror)

  Thrown when HTTP requests to the Workflow backend fail due to network issues, invalid requests, or server errors.

  ### [WorkflowRunFailedError](/errors/runtime-errors#workflowrunfailederror)

  Thrown when a workflow run encounters a fatal error during execution. Contains the underlying error details.

  ### [WorkflowRunNotCompletedError](/errors/runtime-errors#workflowrunnotcompletederror)

  Thrown when attempting to access results from a workflow that hasn't completed yet.

  ### [WorkflowRuntimeError](/errors/runtime-errors#workflowruntimeerror)

  Thrown when the runtime encounters internal errors like serialization failures or invalid workflow functions.

  ### [WorkflowRunNotFoundError](/errors/runtime-errors#workflowrunnotfounderror)

  Thrown when attempting to access a workflow run that doesn't exist.

  ### [WorkflowRunCancelledError](/errors/runtime-errors#workflowruncancellederror)

  Thrown when attempting to get results from a cancelled workflow run.

  ### [RunNotSupportedError](/errors/runtime-errors#runnotsupportederror)

  Thrown when a workflow run requires a newer spec version than the current implementation supports.
</Accordion>

## Common Error Patterns

### Catching Specific Error Types

```typescript theme={null}
import { 
  WorkflowAPIError, 
  WorkflowRunFailedError,
  WorkflowRunNotFoundError 
} from 'workflow';

try {
  const run = await getRun(runId);
  const result = await run.result;
} catch (error) {
  if (WorkflowRunNotFoundError.is(error)) {
    console.error('Run does not exist:', error.runId);
  } else if (WorkflowRunFailedError.is(error)) {
    console.error('Run failed:', error.cause.message);
  } else if (WorkflowAPIError.is(error)) {
    console.error('API error:', error.status, error.message);
  }
}
```

### Handling Rate Limits

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

async function callAPI(endpoint: string) {
  "use step";
  
  try {
    const response = await fetch(endpoint);
    
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      throw new RetryableError('Rate limited', {
        retryAfter: retryAfter ? `${retryAfter}s` : '60s'
      });
    }
    
    return response.json();
  } catch (error) {
    if (WorkflowAPIError.is(error) && error.retryAfter) {
      throw new RetryableError('API rate limited', {
        retryAfter: error.retryAfter * 1000 // Convert seconds to ms
      });
    }
    throw error;
  }
}
```

### Exponential Backoff

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

async function unreliableOperation() {
  "use step";
  
  const metadata = getStepMetadata();
  
  try {
    return await performOperation();
  } catch (error) {
    // Exponential backoff: 1s, 4s, 16s, 64s...
    const delayMs = (metadata.attempt ** 2) * 1000;
    throw new RetryableError('Operation failed, retrying...', {
      retryAfter: delayMs
    });
  }
}

unreliableOperation.maxRetries = 5;
```

## Best Practices

1. **Use FatalError for permanent failures** - Don't waste retries on errors that won't succeed
2. **Use RetryableError for known delays** - Respect rate limits and backpressure signals
3. **Check error types with `.is()` methods** - Type-safe error checking works across contexts
4. **Set appropriate `maxRetries`** - Default is 3, adjust based on your use case
5. **Make retry logic idempotent** - Steps may execute multiple times
6. **Include context in error messages** - Help debugging by providing relevant details

## Related Resources

* [Errors & Retrying Guide](/concepts/errors-and-retries)
* [Idempotency Guide](/concepts/idempotency)
* [Step Metadata API](/api-reference/workflow/get-step-metadata)
* [Common Error Solutions](/errors)


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