# Error Handling

The Anthropic SDK provides comprehensive error classes for different failure scenarios. All errors extend from the base `AnthropicError` class and provide detailed information for debugging and recovery.

## Error Hierarchy

```typescript { .api }
AnthropicError
├── APIError<TStatus, THeaders, TError>
│   ├── BadRequestError (400)
│   ├── AuthenticationError (401)
│   ├── PermissionDeniedError (403)
│   ├── NotFoundError (404)
│   ├── ConflictError (409)
│   ├── UnprocessableEntityError (422)
│   ├── RateLimitError (429)
│   └── InternalServerError (5xx)
├── APIConnectionError
│   └── APIConnectionTimeoutError
└── APIUserAbortError
```

## Base Error Class

```typescript { .api }
class AnthropicError extends Error {
  readonly name: string;
  readonly message: string;
  readonly cause?: Error;
}
```

All SDK errors inherit from `AnthropicError`.

## API Errors

```typescript { .api }
class APIError<TStatus = number, THeaders = Headers, TError = any> extends AnthropicError {
  readonly status: TStatus;           // HTTP status code
  readonly headers: THeaders;         // Response headers
  readonly error: TError;             // Error details from API
  readonly requestID: string | null;  // Request ID for debugging
}
```

**Example:**

```typescript
try {
  const message = await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.APIError) {
    console.error('Status:', error.status);
    console.error('Message:', error.message);
    console.error('Request ID:', error.requestID);
    console.error('Error details:', error.error);
  }
}
```

## HTTP Status Errors

### BadRequestError (400)

Invalid request parameters or malformed request.

```typescript { .api }
class BadRequestError extends APIError<400> {}
```

**Common Causes**:
- Invalid parameter values
- Missing required fields
- Malformed JSON
- Invalid model name

**Example:**

```typescript
try {
  const message = await client.messages.create({
    model: 'invalid-model',
    max_tokens: 1024,
    messages: [{ role: 'user', content: 'Hello' }],
  });
} catch (error) {
  if (error instanceof Anthropic.BadRequestError) {
    console.error('Invalid request:', error.message);
    // "model: invalid-model is not a valid model"
  }
}
```

### AuthenticationError (401)

Authentication failed - invalid or missing API key.

```typescript { .api }
class AuthenticationError extends APIError<401> {}
```

**Common Causes**:
- Missing API key
- Invalid API key
- Expired API key

**Example:**

```typescript
try {
  const client = new Anthropic({ apiKey: 'invalid-key' });
  await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.AuthenticationError) {
    console.error('Authentication failed');
    // Check API key configuration
  }
}
```

### PermissionDeniedError (403)

Insufficient permissions for the requested operation.

```typescript { .api }
class PermissionDeniedError extends APIError<403> {}
```

**Common Causes**:
- Account doesn't have access to requested feature
- API key lacks required permissions

**Example:**

```typescript
try {
  await client.beta.messages.create({
    betas: ['restricted-feature'],
    /* ... */
  });
} catch (error) {
  if (error instanceof Anthropic.PermissionDeniedError) {
    console.error('Access denied to beta feature');
  }
}
```

### NotFoundError (404)

Requested resource not found.

```typescript { .api }
class NotFoundError extends APIError<404> {}
```

**Common Causes**:
- Invalid resource ID
- Resource was deleted
- Typo in endpoint or ID

**Example:**

```typescript
try {
  const batch = await client.messages.batches.retrieve('nonexistent-id');
} catch (error) {
  if (error instanceof Anthropic.NotFoundError) {
    console.error('Batch not found');
  }
}
```

### ConflictError (409)

Request conflicts with current state.

```typescript { .api }
class ConflictError extends APIError<409> {}
```

**Common Causes**:
- Duplicate resource creation
- Conflicting operations

**Example:**

```typescript
try {
  await client.beta.skills.create({
    name: 'existing-skill',  // Already exists
    /* ... */
  });
} catch (error) {
  if (error instanceof Anthropic.ConflictError) {
    console.error('Skill already exists');
  }
}
```

### UnprocessableEntityError (422)

Request is syntactically correct but semantically invalid.

```typescript { .api }
class UnprocessableEntityError extends APIError<422> {}
```

**Common Causes**:
- Invalid parameter combination
- Business logic validation failure

**Example:**

```typescript
try {
  await client.messages.create({
    model: 'claude-sonnet-4-5-20250929',
    max_tokens: 1024,
    messages: [
      { role: 'assistant', content: 'Cannot start with assistant' },
    ],
  });
} catch (error) {
  if (error instanceof Anthropic.UnprocessableEntityError) {
    console.error('Invalid message structure');
  }
}
```

### RateLimitError (429)

Rate limit exceeded.

```typescript { .api }
class RateLimitError extends APIError<429> {}
```

**Common Causes**:
- Too many requests in time window
- Exceeded tokens per minute
- Exceeded requests per day

**Example:**

```typescript
try {
  await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.RateLimitError) {
    console.error('Rate limited');

    // Get retry information from headers
    const retryAfter = error.headers.get('retry-after');
    const resetTime = error.headers.get('x-ratelimit-reset');

    console.log(`Retry after: ${retryAfter} seconds`);
    console.log(`Resets at: ${new Date(parseInt(resetTime) * 1000)}`);

    // Implement backoff
    await new Promise(resolve => setTimeout(resolve, parseInt(retryAfter) * 1000));
  }
}
```

### InternalServerError (5xx)

Server-side error.

```typescript { .api }
class InternalServerError extends APIError<500 | 502 | 503 | 504> {}
```

**Common Causes**:
- Temporary server issues
- Service overload
- Gateway errors

**Example:**

```typescript
try {
  await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.InternalServerError) {
    console.error('Server error:', error.status);

    // Retry with exponential backoff
    await retryWithBackoff(() => client.messages.create({ /* ... */ }));
  }
}
```

## Network Errors

### APIConnectionError

Failed to connect to the API.

```typescript { .api }
class APIConnectionError extends AnthropicError {
  readonly cause?: Error;
}
```

**Common Causes**:
- Network connectivity issues
- DNS resolution failure
- Firewall blocking requests

**Example:**

```typescript
try {
  await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.APIConnectionError) {
    console.error('Network error:', error.message);
    console.error('Cause:', error.cause);

    // Check network connectivity
    // Retry with exponential backoff
  }
}
```

### APIConnectionTimeoutError

Request timed out.

```typescript { .api }
class APIConnectionTimeoutError extends APIConnectionError {}
```

**Common Causes**:
- Request exceeded timeout
- Slow network connection
- Large request/response

**Example:**

```typescript
try {
  await client.messages.create(
    {
      model: 'claude-sonnet-4-5-20250929',
      max_tokens: 10000,
      messages: [{ role: 'user', content: 'Long task' }],
    },
    { timeout: 30000 }  // 30 seconds
  );
} catch (error) {
  if (error instanceof Anthropic.APIConnectionTimeoutError) {
    console.error('Request timed out');

    // Use streaming for long requests
    const stream = client.messages.stream({ /* ... */ });
  }
}
```

### APIUserAbortError

User aborted the request.

```typescript { .api }
class APIUserAbortError extends AnthropicError {}
```

**Common Causes**:
- User called `stream.abort()`
- AbortController signal triggered
- Stream break

**Example:**

```typescript
const stream = client.messages.stream({ /* ... */ });

// User decides to cancel
setTimeout(() => stream.abort(), 5000);

try {
  await stream.done();
} catch (error) {
  if (error instanceof Anthropic.APIUserAbortError) {
    console.log('User canceled the request');
  }
}
```

## Error Handling Patterns

### Basic Try-Catch

```typescript
try {
  const message = await client.messages.create({ /* ... */ });
  console.log(message.content);
} catch (error) {
  if (error instanceof Anthropic.APIError) {
    console.error(`API Error ${error.status}:`, error.message);
  } else if (error instanceof Anthropic.APIConnectionError) {
    console.error('Connection error:', error.message);
  } else {
    console.error('Unexpected error:', error);
    throw error;
  }
}
```

### Comprehensive Error Handling

```typescript
async function createMessage(params: Anthropic.MessageCreateParams) {
  try {
    return await client.messages.create(params);
  } catch (error) {
    // HTTP errors
    if (error instanceof Anthropic.BadRequestError) {
      console.error('Invalid request parameters');
      throw new Error('Please check your request parameters');
    }

    if (error instanceof Anthropic.AuthenticationError) {
      console.error('Authentication failed');
      throw new Error('Please check your API key');
    }

    if (error instanceof Anthropic.RateLimitError) {
      const retryAfter = error.headers.get('retry-after');
      console.error(`Rate limited. Retry after ${retryAfter}s`);
      await sleep(parseInt(retryAfter) * 1000);
      return createMessage(params);  // Retry
    }

    if (error instanceof Anthropic.InternalServerError) {
      console.error('Server error, retrying...');
      await sleep(5000);
      return createMessage(params);  // Retry
    }

    // Network errors
    if (error instanceof Anthropic.APIConnectionTimeoutError) {
      console.error('Request timed out');
      throw new Error('Request took too long. Try using streaming.');
    }

    if (error instanceof Anthropic.APIConnectionError) {
      console.error('Network error');
      throw new Error('Cannot reach Anthropic API. Check your connection.');
    }

    // User abort
    if (error instanceof Anthropic.APIUserAbortError) {
      console.log('Request was canceled');
      return null;
    }

    // Unknown error
    console.error('Unexpected error:', error);
    throw error;
  }
}
```

### Retry with Exponential Backoff

```typescript
async function retryWithBackoff<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  initialDelay = 1000
): Promise<T> {
  let lastError: Error;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;

      // Don't retry on client errors (4xx except 429)
      if (
        error instanceof Anthropic.BadRequestError ||
        error instanceof Anthropic.AuthenticationError ||
        error instanceof Anthropic.PermissionDeniedError ||
        error instanceof Anthropic.NotFoundError
      ) {
        throw error;
      }

      // Retry on server errors, network errors, and rate limits
      if (
        error instanceof Anthropic.RateLimitError ||
        error instanceof Anthropic.InternalServerError ||
        error instanceof Anthropic.APIConnectionError
      ) {
        const delay = initialDelay * Math.pow(2, attempt);
        console.log(`Retry attempt ${attempt + 1} after ${delay}ms`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      // Unknown error
      throw error;
    }
  }

  throw lastError;
}

// Usage
const message = await retryWithBackoff(() =>
  client.messages.create({ /* ... */ })
);
```

### Request ID Logging

```typescript
try {
  const message = await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.APIError) {
    console.error('Error details:', {
      status: error.status,
      message: error.message,
      requestID: error.requestID,  // Include in support requests
      timestamp: new Date().toISOString(),
    });

    // Log for debugging
    logger.error('Anthropic API error', {
      requestId: error.requestID,
      status: error.status,
      error: error.error,
    });
  }
}
```

### Graceful Degradation

```typescript
async function getResponse(prompt: string): Promise<string> {
  try {
    // Try with Opus first
    const message = await client.messages.create({
      model: 'claude-opus-4-5-20250514',
      max_tokens: 2048,
      messages: [{ role: 'user', content: prompt }],
    });
    return message.content[0].text;
  } catch (error) {
    if (error instanceof Anthropic.RateLimitError) {
      console.warn('Opus rate limited, falling back to Sonnet');

      // Fall back to Sonnet
      const message = await client.messages.create({
        model: 'claude-sonnet-4-5-20250929',
        max_tokens: 2048,
        messages: [{ role: 'user', content: prompt }],
      });
      return message.content[0].text;
    }

    throw error;
  }
}
```

## Accessing Error Details

### Error Object Structure

```typescript
catch (error) {
  if (error instanceof Anthropic.APIError) {
    // Status code
    console.log(error.status);  // 400, 401, 429, etc.

    // Error message
    console.log(error.message);  // Human-readable message

    // Request ID
    console.log(error.requestID);  // 'req_...'

    // Response headers
    console.log(error.headers.get('x-ratelimit-remaining'));

    // Detailed error from API
    console.log(error.error);  // API error object

    // Stack trace
    console.log(error.stack);

    // Original cause (if wrapped)
    console.log(error.cause);
  }
}
```

### API Error Object

```typescript
if (error instanceof Anthropic.APIError) {
  const apiError = error.error;

  console.log('Type:', apiError.type);
  console.log('Message:', apiError.message);

  // Error-specific fields
  if (apiError.type === 'invalid_request_error') {
    console.log('Invalid field:', apiError.field);
    console.log('Invalid value:', apiError.value);
  }
}
```

## Best Practices

### Specific Error Handling

```typescript
// ✅ Good: Handle specific errors
try {
  const message = await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.RateLimitError) {
    // Handle rate limiting
  } else if (error instanceof Anthropic.AuthenticationError) {
    // Handle auth issues
  } else if (error instanceof Anthropic.APIError) {
    // Handle other API errors
  } else {
    // Handle unexpected errors
  }
}

// ❌ Bad: Generic catch-all
try {
  const message = await client.messages.create({ /* ... */ });
} catch (error) {
  console.error('Error:', error);  // Not helpful
}
```

### Error Logging

```typescript
// ✅ Good: Log useful context
try {
  const message = await client.messages.create(params);
} catch (error) {
  if (error instanceof Anthropic.APIError) {
    logger.error('Anthropic API request failed', {
      requestId: error.requestID,
      status: error.status,
      message: error.message,
      params: { model: params.model, max_tokens: params.max_tokens },
      userId: currentUserId,
    });
  }
  throw error;
}
```

### User-Friendly Messages

```typescript
// ✅ Good: User-friendly error messages
try {
  const message = await client.messages.create({ /* ... */ });
} catch (error) {
  if (error instanceof Anthropic.RateLimitError) {
    return {
      error: 'Too many requests. Please try again in a few moments.',
      retryAfter: error.headers.get('retry-after'),
    };
  } else if (error instanceof Anthropic.APIConnectionError) {
    return {
      error: 'Unable to connect to service. Please check your internet connection.',
    };
  } else {
    return {
      error: 'An unexpected error occurred. Please try again later.',
    };
  }
}
```

## See Also

- [Client Configuration](./client.md) - Timeout and retry settings
- [Messages API](./messages.md) - Message creation errors
- [Streaming](./streaming.md) - Stream error handling
- [Types](./types.md) - Error type definitions
