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

# FatalError

> Throw a non-retryable error from a step function

A fatal error that cannot be retried. When thrown from a step function, it causes the step to fail immediately and the error is bubbled up to the workflow logic without any retry attempts.

## Constructor

```typescript theme={null}
new FatalError(message: string)
```

## Parameters

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

## Properties

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

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

<ResponseField name="fatal" type="boolean">
  Always set to `true`. Used internally to identify fatal errors.
</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 FatalError.

  ```typescript theme={null}
  if (FatalError.is(error)) {
    console.log('This is a fatal error');
  }
  ```
</ResponseField>

## Usage

### Basic Fatal Error

Throw an error that should not be retried:

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

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

  try {
    await step(async () => {
      "use step";
      
      const user = await getUser();
      
      if (!user) {
        // User not found - retrying won't help
        throw new FatalError('User not found');
      }
      
      return user;
    });
  } catch (error) {
    if (FatalError.is(error)) {
      console.log('Fatal error occurred:', error.message);
      // Handle the fatal error
    }
  }
}
```

### Validation Errors

Mark validation errors as fatal:

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

export async function processPayment(amount: number) {
  "use workflow";

  await step(async () => {
    "use step";
    
    if (amount <= 0) {
      throw new FatalError('Invalid payment amount');
    }
    
    if (amount > 10000) {
      throw new FatalError('Payment amount exceeds limit');
    }
    
    await chargePayment(amount);
  });
}
```

### Invalid Configuration

Fail fast on configuration errors:

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

export async function workflowWithConfig(config: any) {
  "use workflow";

  await step(async () => {
    "use step";
    
    if (!config.apiKey) {
      throw new FatalError('API key is required in configuration');
    }
    
    if (!config.endpoint) {
      throw new FatalError('API endpoint is required in configuration');
    }
    
    await initializeService(config);
  });
}
```

### Conditional Fatal vs Retryable

Decide whether to retry based on error type:

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

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

  await step(async () => {
    "use step";
    
    try {
      await apiCall();
    } catch (error: any) {
      // Client errors (4xx) are fatal
      if (error.status >= 400 && error.status < 500) {
        throw new FatalError(`Client error: ${error.message}`);
      }
      
      // Server errors (5xx) are retryable
      if (error.status >= 500) {
        throw new RetryableError(
          `Server error: ${error.message}`,
          { retryAfter: '5s' }
        );
      }
      
      throw error;
    }
  });
}
```

### Permission Errors

Mark permission errors as fatal:

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

export async function accessResource(resourceId: string) {
  "use workflow";

  await step(async () => {
    "use step";
    
    const hasPermission = await checkPermission(resourceId);
    
    if (!hasPermission) {
      throw new FatalError('Access denied: insufficient permissions');
    }
    
    return await getResource(resourceId);
  });
}
```

### Type Guard Usage

Check if an error is fatal:

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

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

  try {
    await step(async () => {
      "use step";
      await riskyOperation();
    });
  } catch (error) {
    if (FatalError.is(error)) {
      console.log('Fatal error - not retrying');
      await sendAlert(error.message);
    } else {
      console.log('Retryable error occurred');
    }
  }
}
```

### Business Logic Errors

Fail on business rule violations:

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

export async function processOrder(orderId: string) {
  "use workflow";

  await step(async () => {
    "use step";
    
    const order = await getOrder(orderId);
    
    if (order.status === 'cancelled') {
      throw new FatalError('Cannot process cancelled order');
    }
    
    if (order.items.length === 0) {
      throw new FatalError('Order has no items');
    }
    
    await fulfillOrder(order);
  });
}
```

### Quota Exceeded

Mark quota errors as fatal:

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

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

  await step(async () => {
    "use step";
    
    const quota = await getQuota();
    
    if (quota.remaining === 0) {
      throw new FatalError(
        `Quota exceeded. Resets at ${quota.resetTime}`
      );
    }
    
    await performAction();
  });
}
```

## When to Use FatalError

Use `FatalError` when:

* **Validation fails**: Invalid input that won't change on retry
* **Authorization fails**: Permission errors that retrying won't fix
* **Business rules violated**: Logic errors that are permanent
* **Resource not found**: Missing resources that won't appear on retry
* **Configuration errors**: Invalid settings that need manual correction
* **Client errors (4xx)**: HTTP client errors that indicate bad requests

Do NOT use `FatalError` for:

* **Network timeouts**: Use `RetryableError` instead
* **Server errors (5xx)**: Use `RetryableError` instead
* **Rate limits**: Use `RetryableError` with appropriate `retryAfter`
* **Temporary failures**: Anything that might succeed on retry

## Notes

* Fatal errors cause the step to fail immediately without retries
* The error is bubbled up to the workflow logic and can be caught with try/catch
* Unlike normal errors, fatal errors bypass the default retry mechanism
* The `fatal` property is used internally to identify fatal errors
* Use `FatalError.is()` to check if an error is fatal

## Related

* [RetryableError](/api-reference/workflow/retryable-error) - Throw an error with retry configuration
* [getStepMetadata](/api-reference/workflow/get-step-metadata) - Get current step attempt number


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