0
# Error Handling
1
2
Comprehensive error types and handling strategies for different failure scenarios in the Slack Web API.
3
4
## Capabilities
5
6
### Error Types Overview
7
8
The @slack/web-api package provides specific error types for different failure scenarios, allowing you to handle errors appropriately based on their cause.
9
10
```typescript { .api }
11
/**
12
* Base interface for all coded errors
13
*/
14
interface CodedError extends NodeJS.ErrnoException {
15
code: ErrorCode;
16
}
17
18
/**
19
* Union type of all Web API specific errors
20
*/
21
type WebAPICallError = WebAPIPlatformError | WebAPIRequestError | WebAPIHTTPError | WebAPIRateLimitedError;
22
23
/**
24
* Error codes for categorizing different types of failures
25
*/
26
enum ErrorCode {
27
RequestError = 'slack_webapi_request_error',
28
HTTPError = 'slack_webapi_http_error',
29
PlatformError = 'slack_webapi_platform_error',
30
RateLimitedError = 'slack_webapi_rate_limited_error',
31
FileUploadInvalidArgumentsError = 'slack_webapi_file_upload_invalid_args_error',
32
FileUploadReadFileDataError = 'slack_webapi_file_upload_read_file_data_error'
33
}
34
```
35
36
### Platform Errors
37
38
Errors returned by Slack's API for business logic issues or invalid requests.
39
40
```typescript { .api }
41
/**
42
* Platform errors from Slack's API
43
*/
44
interface WebAPIPlatformError extends CodedError {
45
code: ErrorCode.PlatformError;
46
data: WebAPICallResult & {
47
error: string;
48
};
49
}
50
```
51
52
**Usage Examples:**
53
54
```typescript
55
import { WebClient, ErrorCode } from "@slack/web-api";
56
57
const web = new WebClient(token);
58
59
try {
60
await web.chat.postMessage({
61
channel: 'nonexistent-channel',
62
text: 'Hello!'
63
});
64
} catch (error) {
65
if (error.code === ErrorCode.PlatformError) {
66
console.log('Slack API error:', error.data.error);
67
68
// Common platform errors:
69
switch (error.data.error) {
70
case 'channel_not_found':
71
console.log('The specified channel does not exist');
72
break;
73
case 'not_in_channel':
74
console.log('Bot is not a member of this channel');
75
break;
76
case 'invalid_auth':
77
console.log('Invalid or expired token');
78
break;
79
case 'missing_scope':
80
console.log('Token lacks required OAuth scope');
81
break;
82
default:
83
console.log('Other platform error:', error.data.error);
84
}
85
}
86
}
87
```
88
89
### HTTP Errors
90
91
Errors related to HTTP transport issues, network problems, or server responses.
92
93
```typescript { .api }
94
/**
95
* HTTP protocol and transport errors
96
*/
97
interface WebAPIHTTPError extends CodedError {
98
code: ErrorCode.HTTPError;
99
statusCode: number;
100
statusMessage: string;
101
headers: IncomingHttpHeaders;
102
body?: any;
103
}
104
```
105
106
**Usage Examples:**
107
108
```typescript
109
try {
110
const result = await web.users.list();
111
} catch (error) {
112
if (error.code === ErrorCode.HTTPError) {
113
console.log(`HTTP ${error.statusCode}: ${error.statusMessage}`);
114
115
// Handle specific HTTP status codes
116
switch (error.statusCode) {
117
case 429:
118
console.log('Rate limited by HTTP layer');
119
break;
120
case 500:
121
case 502:
122
case 503:
123
console.log('Slack server error, retry later');
124
break;
125
case 404:
126
console.log('API endpoint not found');
127
break;
128
default:
129
console.log('Other HTTP error');
130
}
131
}
132
}
133
```
134
135
### Request Errors
136
137
Errors that occur during the HTTP request process, such as network issues or timeouts.
138
139
```typescript { .api }
140
/**
141
* Request-level errors (network, timeout, etc.)
142
*/
143
interface WebAPIRequestError extends CodedError {
144
code: ErrorCode.RequestError;
145
original: Error;
146
}
147
```
148
149
**Usage Examples:**
150
151
```typescript
152
try {
153
const result = await web.conversations.list();
154
} catch (error) {
155
if (error.code === ErrorCode.RequestError) {
156
console.log('Request failed:', error.message);
157
console.log('Original error:', error.original.message);
158
159
// Common request error scenarios
160
if (error.original.code === 'ENOTFOUND') {
161
console.log('DNS resolution failed');
162
} else if (error.original.code === 'ECONNRESET') {
163
console.log('Connection was reset');
164
} else if (error.original.code === 'ETIMEDOUT') {
165
console.log('Request timed out');
166
}
167
}
168
}
169
```
170
171
### Rate Limiting Errors
172
173
Specific handling for when your application hits Slack's rate limits.
174
175
```typescript { .api }
176
/**
177
* Rate limiting specific errors
178
*/
179
interface WebAPIRateLimitedError extends CodedError {
180
code: ErrorCode.RateLimitedError;
181
retryAfter: number;
182
}
183
```
184
185
**Usage Examples:**
186
187
```typescript
188
import { WebClient, ErrorCode, WebClientEvent } from "@slack/web-api";
189
190
const web = new WebClient(token, {
191
// Configure to reject rate limited calls instead of auto-retry
192
rejectRateLimitedCalls: true
193
});
194
195
// Listen for rate limit events
196
web.on(WebClientEvent.RATE_LIMITED, (retryAfter) => {
197
console.log(`Rate limited for ${retryAfter} seconds`);
198
});
199
200
try {
201
await web.chat.postMessage({
202
channel: '#general',
203
text: 'Hello!'
204
});
205
} catch (error) {
206
if (error.code === ErrorCode.RateLimitedError) {
207
console.log(`Rate limited! Retry after ${error.retryAfter} seconds`);
208
209
// Wait and retry
210
await new Promise(resolve => setTimeout(resolve, error.retryAfter * 1000));
211
212
// Retry the request
213
const result = await web.chat.postMessage({
214
channel: '#general',
215
text: 'Hello (retry)!'
216
});
217
}
218
}
219
```
220
221
### File Upload Errors
222
223
Specific errors that can occur during file upload operations.
224
225
```typescript { .api }
226
/**
227
* File upload argument validation errors
228
*/
229
interface WebAPIFileUploadInvalidArgumentsError extends CodedError {
230
code: ErrorCode.FileUploadInvalidArgumentsError;
231
data: WebAPICallResult & {
232
error: string;
233
};
234
}
235
236
/**
237
* Union type for file upload errors
238
*/
239
type WebAPIFilesUploadError = WebAPIFileUploadInvalidArgumentsError;
240
```
241
242
**Usage Examples:**
243
244
```typescript
245
try {
246
await web.filesUploadV2({
247
// Missing required parameters
248
channels: '#general'
249
// No file content provided
250
});
251
} catch (error) {
252
if (error.code === ErrorCode.FileUploadInvalidArgumentsError) {
253
console.log('File upload validation error:', error.data.error);
254
255
// Common validation errors:
256
switch (error.data.error) {
257
case 'no_file_data':
258
console.log('No file content provided');
259
break;
260
case 'invalid_file_type':
261
console.log('File type not allowed');
262
break;
263
case 'file_too_large':
264
console.log('File exceeds size limit');
265
break;
266
default:
267
console.log('Other validation error:', error.data.error);
268
}
269
}
270
}
271
```
272
273
### Error Handling Patterns
274
275
Best practices for handling different error scenarios.
276
277
```typescript { .api }
278
/**
279
* Helper function to check if error is a specific type
280
*/
281
function isWebAPIError(error: any): error is WebAPICallError {
282
return error && typeof error.code === 'string' && error.code.startsWith('slack_webapi_');
283
}
284
285
/**
286
* Helper function to extract error message
287
*/
288
function getErrorMessage(error: WebAPICallError): string {
289
if ('data' in error && error.data.error) {
290
return error.data.error;
291
}
292
return error.message || 'Unknown error';
293
}
294
```
295
296
**Usage Examples:**
297
298
```typescript
299
import { WebClient, ErrorCode } from "@slack/web-api";
300
301
async function postMessageWithRetry(web: WebClient, channel: string, text: string, maxRetries = 3) {
302
let attempt = 0;
303
304
while (attempt < maxRetries) {
305
try {
306
return await web.chat.postMessage({ channel, text });
307
} catch (error) {
308
attempt++;
309
310
if (!isWebAPIError(error)) {
311
// Non-API error, don't retry
312
throw error;
313
}
314
315
switch (error.code) {
316
case ErrorCode.RateLimitedError:
317
// Wait and retry for rate limits
318
console.log(`Rate limited, waiting ${error.retryAfter}s (attempt ${attempt})`);
319
await new Promise(resolve => setTimeout(resolve, error.retryAfter * 1000));
320
continue;
321
322
case ErrorCode.RequestError:
323
// Retry network errors
324
if (attempt < maxRetries) {
325
console.log(`Network error, retrying (attempt ${attempt})`);
326
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
327
continue;
328
}
329
break;
330
331
case ErrorCode.HTTPError:
332
// Retry server errors
333
if (error.statusCode >= 500 && attempt < maxRetries) {
334
console.log(`Server error ${error.statusCode}, retrying (attempt ${attempt})`);
335
await new Promise(resolve => setTimeout(resolve, 2000 * attempt));
336
continue;
337
}
338
break;
339
340
case ErrorCode.PlatformError:
341
// Don't retry platform errors (client issues)
342
console.log('Platform error:', getErrorMessage(error));
343
throw error;
344
}
345
346
// Max retries reached or non-retryable error
347
throw error;
348
}
349
}
350
}
351
352
// Usage
353
try {
354
const result = await postMessageWithRetry(web, '#general', 'Hello with retry logic!');
355
console.log('Message posted:', result.ts);
356
} catch (error) {
357
console.error('Failed to post message after retries:', error.message);
358
}
359
```
360
361
### Custom Error Handling
362
363
Implementing custom error handling strategies for specific use cases.
364
365
```typescript { .api }
366
/**
367
* Custom error handler interface
368
*/
369
interface ErrorHandler {
370
canHandle(error: any): boolean;
371
handle(error: any): Promise<any> | any;
372
}
373
374
/**
375
* Example: Custom handler for channel-related errors
376
*/
377
class ChannelErrorHandler implements ErrorHandler {
378
canHandle(error: any): boolean {
379
return error.code === ErrorCode.PlatformError &&
380
['channel_not_found', 'not_in_channel', 'is_archived'].includes(error.data.error);
381
}
382
383
async handle(error: WebAPIPlatformError): Promise<void> {
384
switch (error.data.error) {
385
case 'channel_not_found':
386
console.log('Channel does not exist, creating it...');
387
// Custom logic to handle missing channel
388
break;
389
case 'not_in_channel':
390
console.log('Bot not in channel, joining...');
391
// Custom logic to join channel
392
break;
393
case 'is_archived':
394
console.log('Channel is archived, unarchiving...');
395
// Custom logic to unarchive channel
396
break;
397
}
398
}
399
}
400
```
401
402
**Usage Examples:**
403
404
```typescript
405
const channelHandler = new ChannelErrorHandler();
406
407
async function postMessageWithCustomHandling(web: WebClient, channel: string, text: string) {
408
try {
409
return await web.chat.postMessage({ channel, text });
410
} catch (error) {
411
if (channelHandler.canHandle(error)) {
412
await channelHandler.handle(error);
413
// Retry after handling
414
return await web.chat.postMessage({ channel, text });
415
}
416
throw error;
417
}
418
}
419
```
420
421
## Types
422
423
```typescript { .api }
424
import type { IncomingHttpHeaders } from 'node:http';
425
426
interface WebAPICallResult {
427
ok: boolean;
428
error?: string;
429
response_metadata?: {
430
warnings?: string[];
431
next_cursor?: string;
432
scopes?: string[];
433
messages?: string[];
434
};
435
}
436
```