Official library for using the Slack Platform's Web API
—
Comprehensive error types and handling strategies for different failure scenarios in the Slack Web API.
The @slack/web-api package provides specific error types for different failure scenarios, allowing you to handle errors appropriately based on their cause.
/**
* 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'
}Errors returned by Slack's API for business logic issues or invalid requests.
/**
* Platform errors from Slack's API
*/
interface WebAPIPlatformError extends CodedError {
code: ErrorCode.PlatformError;
data: WebAPICallResult & {
error: string;
};
}Usage Examples:
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);
}
}
}Errors related to HTTP transport issues, network problems, or server responses.
/**
* HTTP protocol and transport errors
*/
interface WebAPIHTTPError extends CodedError {
code: ErrorCode.HTTPError;
statusCode: number;
statusMessage: string;
headers: IncomingHttpHeaders;
body?: any;
}Usage Examples:
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');
}
}
}Errors that occur during the HTTP request process, such as network issues or timeouts.
/**
* Request-level errors (network, timeout, etc.)
*/
interface WebAPIRequestError extends CodedError {
code: ErrorCode.RequestError;
original: Error;
}Usage Examples:
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');
}
}
}Specific handling for when your application hits Slack's rate limits.
/**
* Rate limiting specific errors
*/
interface WebAPIRateLimitedError extends CodedError {
code: ErrorCode.RateLimitedError;
retryAfter: number;
}Usage Examples:
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)!'
});
}
}Specific errors that can occur during file upload operations.
/**
* 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:
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);
}
}
}Best practices for handling different error scenarios.
/**
* 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:
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);
}Implementing custom error handling strategies for specific use cases.
/**
* 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:
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;
}
}import type { IncomingHttpHeaders } from 'node:http';
interface WebAPICallResult {
ok: boolean;
error?: string;
response_metadata?: {
warnings?: string[];
next_cursor?: string;
scopes?: string[];
messages?: string[];
};
}Install with Tessl CLI
npx tessl i tessl/npm-slack--web-api