# Error Handling

Comprehensive error types and handling strategies for different failure scenarios in the Slack Web API.

## Capabilities

### Error Types Overview

The @slack/web-api package provides specific error types for different failure scenarios, allowing you to handle errors appropriately based on their cause.

```typescript { .api }
/**
 * Base interface for all coded errors
 */
interface CodedError extends NodeJS.ErrnoException {
  code: ErrorCode;
}

/**
 * Union type of all Web API specific errors
 */
type WebAPICallError = WebAPIPlatformError | WebAPIRequestError | WebAPIHTTPError | WebAPIRateLimitedError;

/**
 * Error codes for categorizing different types of failures
 */
enum ErrorCode {
  RequestError = 'slack_webapi_request_error',
  HTTPError = 'slack_webapi_http_error',
  PlatformError = 'slack_webapi_platform_error',
  RateLimitedError = 'slack_webapi_rate_limited_error',
  FileUploadInvalidArgumentsError = 'slack_webapi_file_upload_invalid_args_error',
  FileUploadReadFileDataError = 'slack_webapi_file_upload_read_file_data_error'
}
```

### Platform Errors

Errors returned by Slack's API for business logic issues or invalid requests.

```typescript { .api }
/**
 * Platform errors from Slack's API
 */
interface WebAPIPlatformError extends CodedError {
  code: ErrorCode.PlatformError;
  data: WebAPICallResult & {
    error: string;
  };
}
```

**Usage Examples:**

```typescript
import { WebClient, ErrorCode } from "@slack/web-api";

const web = new WebClient(token);

try {
  await web.chat.postMessage({
    channel: 'nonexistent-channel',
    text: 'Hello!'
  });
} catch (error) {
  if (error.code === ErrorCode.PlatformError) {
    console.log('Slack API error:', error.data.error);
    
    // Common platform errors:
    switch (error.data.error) {
      case 'channel_not_found':
        console.log('The specified channel does not exist');
        break;
      case 'not_in_channel':
        console.log('Bot is not a member of this channel');
        break;
      case 'invalid_auth':
        console.log('Invalid or expired token');
        break;
      case 'missing_scope':
        console.log('Token lacks required OAuth scope');
        break;
      default:
        console.log('Other platform error:', error.data.error);
    }
  }
}
```

### HTTP Errors

Errors related to HTTP transport issues, network problems, or server responses.

```typescript { .api }
/**
 * HTTP protocol and transport errors
 */
interface WebAPIHTTPError extends CodedError {
  code: ErrorCode.HTTPError;
  statusCode: number;
  statusMessage: string;
  headers: IncomingHttpHeaders;
  body?: any;
}
```

**Usage Examples:**

```typescript
try {
  const result = await web.users.list();
} catch (error) {
  if (error.code === ErrorCode.HTTPError) {
    console.log(`HTTP ${error.statusCode}: ${error.statusMessage}`);
    
    // Handle specific HTTP status codes
    switch (error.statusCode) {
      case 429:
        console.log('Rate limited by HTTP layer');
        break;
      case 500:
      case 502:
      case 503:
        console.log('Slack server error, retry later');
        break;
      case 404:
        console.log('API endpoint not found');
        break;
      default:
        console.log('Other HTTP error');
    }
  }
}
```

### Request Errors

Errors that occur during the HTTP request process, such as network issues or timeouts.

```typescript { .api }
/**
 * Request-level errors (network, timeout, etc.)
 */
interface WebAPIRequestError extends CodedError {
  code: ErrorCode.RequestError;
  original: Error;
}
```

**Usage Examples:**

```typescript
try {
  const result = await web.conversations.list();
} catch (error) {
  if (error.code === ErrorCode.RequestError) {
    console.log('Request failed:', error.message);
    console.log('Original error:', error.original.message);
    
    // Common request error scenarios
    if (error.original.code === 'ENOTFOUND') {
      console.log('DNS resolution failed');
    } else if (error.original.code === 'ECONNRESET') {
      console.log('Connection was reset');
    } else if (error.original.code === 'ETIMEDOUT') {
      console.log('Request timed out');
    }
  }
}
```

### Rate Limiting Errors

Specific handling for when your application hits Slack's rate limits.

```typescript { .api }
/**
 * Rate limiting specific errors
 */
interface WebAPIRateLimitedError extends CodedError {
  code: ErrorCode.RateLimitedError;
  retryAfter: number;
}
```

**Usage Examples:**

```typescript
import { WebClient, ErrorCode, WebClientEvent } from "@slack/web-api";

const web = new WebClient(token, {
  // Configure to reject rate limited calls instead of auto-retry
  rejectRateLimitedCalls: true
});

// Listen for rate limit events
web.on(WebClientEvent.RATE_LIMITED, (retryAfter) => {
  console.log(`Rate limited for ${retryAfter} seconds`);
});

try {
  await web.chat.postMessage({
    channel: '#general',
    text: 'Hello!'
  });
} catch (error) {
  if (error.code === ErrorCode.RateLimitedError) {
    console.log(`Rate limited! Retry after ${error.retryAfter} seconds`);
    
    // Wait and retry
    await new Promise(resolve => setTimeout(resolve, error.retryAfter * 1000));
    
    // Retry the request
    const result = await web.chat.postMessage({
      channel: '#general',
      text: 'Hello (retry)!'
    });
  }
}
```

### File Upload Errors

Specific errors that can occur during file upload operations.

```typescript { .api }
/**
 * File upload argument validation errors
 */
interface WebAPIFileUploadInvalidArgumentsError extends CodedError {
  code: ErrorCode.FileUploadInvalidArgumentsError;
  data: WebAPICallResult & {
    error: string;
  };
}

/**
 * Union type for file upload errors
 */
type WebAPIFilesUploadError = WebAPIFileUploadInvalidArgumentsError;
```

**Usage Examples:**

```typescript
try {
  await web.filesUploadV2({
    // Missing required parameters
    channels: '#general'
    // No file content provided
  });
} catch (error) {
  if (error.code === ErrorCode.FileUploadInvalidArgumentsError) {
    console.log('File upload validation error:', error.data.error);
    
    // Common validation errors:
    switch (error.data.error) {
      case 'no_file_data':
        console.log('No file content provided');
        break;
      case 'invalid_file_type':
        console.log('File type not allowed');
        break;
      case 'file_too_large':
        console.log('File exceeds size limit');
        break;
      default:
        console.log('Other validation error:', error.data.error);
    }
  }
}
```

### Error Handling Patterns

Best practices for handling different error scenarios.

```typescript { .api }
/**
 * Helper function to check if error is a specific type
 */
function isWebAPIError(error: any): error is WebAPICallError {
  return error && typeof error.code === 'string' && error.code.startsWith('slack_webapi_');
}

/**
 * Helper function to extract error message
 */
function getErrorMessage(error: WebAPICallError): string {
  if ('data' in error && error.data.error) {
    return error.data.error;
  }
  return error.message || 'Unknown error';
}
```

**Usage Examples:**

```typescript
import { WebClient, ErrorCode } from "@slack/web-api";

async function postMessageWithRetry(web: WebClient, channel: string, text: string, maxRetries = 3) {
  let attempt = 0;
  
  while (attempt < maxRetries) {
    try {
      return await web.chat.postMessage({ channel, text });
    } catch (error) {
      attempt++;
      
      if (!isWebAPIError(error)) {
        // Non-API error, don't retry
        throw error;
      }
      
      switch (error.code) {
        case ErrorCode.RateLimitedError:
          // Wait and retry for rate limits
          console.log(`Rate limited, waiting ${error.retryAfter}s (attempt ${attempt})`);
          await new Promise(resolve => setTimeout(resolve, error.retryAfter * 1000));
          continue;
          
        case ErrorCode.RequestError:
          // Retry network errors
          if (attempt < maxRetries) {
            console.log(`Network error, retrying (attempt ${attempt})`);
            await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
            continue;
          }
          break;
          
        case ErrorCode.HTTPError:
          // Retry server errors
          if (error.statusCode >= 500 && attempt < maxRetries) {
            console.log(`Server error ${error.statusCode}, retrying (attempt ${attempt})`);
            await new Promise(resolve => setTimeout(resolve, 2000 * attempt));
            continue;
          }
          break;
          
        case ErrorCode.PlatformError:
          // Don't retry platform errors (client issues)
          console.log('Platform error:', getErrorMessage(error));
          throw error;
      }
      
      // Max retries reached or non-retryable error
      throw error;
    }
  }
}

// Usage
try {
  const result = await postMessageWithRetry(web, '#general', 'Hello with retry logic!');
  console.log('Message posted:', result.ts);
} catch (error) {
  console.error('Failed to post message after retries:', error.message);
}
```

### Custom Error Handling

Implementing custom error handling strategies for specific use cases.

```typescript { .api }
/**
 * Custom error handler interface
 */
interface ErrorHandler {
  canHandle(error: any): boolean;
  handle(error: any): Promise<any> | any;
}

/**
 * Example: Custom handler for channel-related errors
 */
class ChannelErrorHandler implements ErrorHandler {
  canHandle(error: any): boolean {
    return error.code === ErrorCode.PlatformError && 
           ['channel_not_found', 'not_in_channel', 'is_archived'].includes(error.data.error);
  }
  
  async handle(error: WebAPIPlatformError): Promise<void> {
    switch (error.data.error) {
      case 'channel_not_found':
        console.log('Channel does not exist, creating it...');
        // Custom logic to handle missing channel
        break;
      case 'not_in_channel':
        console.log('Bot not in channel, joining...');
        // Custom logic to join channel
        break;
      case 'is_archived':
        console.log('Channel is archived, unarchiving...');
        // Custom logic to unarchive channel
        break;
    }
  }
}
```

**Usage Examples:**

```typescript
const channelHandler = new ChannelErrorHandler();

async function postMessageWithCustomHandling(web: WebClient, channel: string, text: string) {
  try {
    return await web.chat.postMessage({ channel, text });
  } catch (error) {
    if (channelHandler.canHandle(error)) {
      await channelHandler.handle(error);
      // Retry after handling
      return await web.chat.postMessage({ channel, text });
    }
    throw error;
  }
}
```

## Types

```typescript { .api }
import type { IncomingHttpHeaders } from 'node:http';

interface WebAPICallResult {
  ok: boolean;
  error?: string;
  response_metadata?: {
    warnings?: string[];
    next_cursor?: string;
    scopes?: string[];
    messages?: string[];
  };
}
```