docs
0
# Error Handling
1
2
Comprehensive error classes and handling patterns for different failure scenarios in the Mux Node SDK including network errors, authentication issues, and API-specific errors.
3
4
## Error Class Hierarchy
5
6
All Mux SDK errors extend from the base MuxError class, providing consistent error handling across all operations.
7
8
```typescript { .api }
9
/**
10
* Base error class for all Mux SDK errors
11
*/
12
class MuxError extends Error {
13
/** Error name identifier */
14
override readonly name: string;
15
/** Error message */
16
override readonly message: string;
17
/** Stack trace */
18
override readonly stack?: string;
19
}
20
21
/**
22
* Generic API error with HTTP details
23
*/
24
class APIError extends MuxError {
25
/** HTTP status code */
26
readonly status: number | undefined;
27
/** Response headers */
28
readonly headers: Core.Headers | undefined;
29
/** Error response body */
30
readonly error: Object | undefined;
31
}
32
```
33
34
### HTTP Status Error Classes
35
36
Specific error classes for different HTTP status codes with detailed context information.
37
38
```typescript { .api }
39
/**
40
* 400 Bad Request - Invalid request parameters
41
*/
42
class BadRequestError extends APIError {
43
override readonly status: 400;
44
}
45
46
/**
47
* 401 Unauthorized - Authentication failed
48
*/
49
class AuthenticationError extends APIError {
50
override readonly status: 401;
51
}
52
53
/**
54
* 403 Forbidden - Access denied for resource
55
*/
56
class PermissionDeniedError extends APIError {
57
override readonly status: 403;
58
}
59
60
/**
61
* 404 Not Found - Resource not found
62
*/
63
class NotFoundError extends APIError {
64
override readonly status: 404;
65
}
66
67
/**
68
* 409 Conflict - Resource conflict (duplicate creation, etc.)
69
*/
70
class ConflictError extends APIError {
71
override readonly status: 409;
72
}
73
74
/**
75
* 422 Unprocessable Entity - Validation errors
76
*/
77
class UnprocessableEntityError extends APIError {
78
override readonly status: 422;
79
}
80
81
/**
82
* 429 Too Many Requests - Rate limit exceeded
83
*/
84
class RateLimitError extends APIError {
85
override readonly status: 429;
86
/** Retry after seconds */
87
readonly retryAfter?: number;
88
}
89
90
/**
91
* 500+ Server Error - Server-side errors
92
*/
93
class InternalServerError extends APIError {
94
override readonly status: number; // 500, 502, 503, etc.
95
}
96
```
97
98
### Network and Connection Errors
99
100
Error classes for network-related failures and connection issues.
101
102
```typescript { .api }
103
/**
104
* Network connection failure
105
*/
106
class APIConnectionError extends MuxError {
107
/** Original network error */
108
readonly cause?: Error;
109
}
110
111
/**
112
* Request timeout
113
*/
114
class APIConnectionTimeoutError extends APIConnectionError {
115
/** Timeout duration in milliseconds */
116
readonly timeout?: number;
117
}
118
119
/**
120
* Request aborted by user
121
*/
122
class APIUserAbortError extends APIConnectionError {
123
/** Abort reason */
124
readonly reason?: string;
125
}
126
```
127
128
## Error Handling Patterns
129
130
### Basic Error Handling
131
132
```typescript
133
import {
134
MuxError,
135
AuthenticationError,
136
NotFoundError,
137
BadRequestError,
138
} from '@mux/mux-node';
139
140
async function handleBasicErrors() {
141
try {
142
const asset = await mux.video.assets.retrieve('asset-id');
143
console.log('Asset retrieved:', asset.id);
144
} catch (error) {
145
if (error instanceof AuthenticationError) {
146
console.error('Authentication failed - check your token credentials');
147
} else if (error instanceof NotFoundError) {
148
console.error('Asset not found');
149
} else if (error instanceof BadRequestError) {
150
console.error('Invalid request parameters');
151
} else if (error instanceof MuxError) {
152
console.error('Mux API error:', error.message);
153
} else {
154
console.error('Unexpected error:', error);
155
}
156
}
157
}
158
```
159
160
### Detailed Error Information
161
162
```typescript
163
import { APIError } from '@mux/mux-node';
164
165
async function handleDetailedErrors() {
166
try {
167
const asset = await mux.video.assets.create({
168
inputs: [{ url: 'invalid-url' }],
169
});
170
} catch (error) {
171
if (error instanceof APIError) {
172
console.error('API Error Details:');
173
console.error('Status:', error.status);
174
console.error('Message:', error.message);
175
console.error('Headers:', error.headers);
176
console.error('Error Body:', error.error);
177
178
// Access detailed error information
179
if (error.error && typeof error.error === 'object') {
180
const errorBody = error.error as any;
181
console.error('Error Code:', errorBody.code);
182
console.error('Error Details:', errorBody.details);
183
}
184
}
185
}
186
}
187
```
188
189
### Retry Logic with Error Handling
190
191
```typescript
192
import {
193
RateLimitError,
194
APIConnectionTimeoutError,
195
InternalServerError,
196
} from '@mux/mux-node';
197
198
async function withRetry<T>(
199
operation: () => Promise<T>,
200
maxRetries: number = 3
201
): Promise<T> {
202
let lastError: Error;
203
204
for (let attempt = 1; attempt <= maxRetries; attempt++) {
205
try {
206
return await operation();
207
} catch (error) {
208
lastError = error as Error;
209
210
// Determine if error is retryable
211
const isRetryable = error instanceof RateLimitError ||
212
error instanceof APIConnectionTimeoutError ||
213
error instanceof InternalServerError;
214
215
if (!isRetryable || attempt === maxRetries) {
216
throw error;
217
}
218
219
// Calculate delay based on error type
220
let delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
221
222
if (error instanceof RateLimitError && error.retryAfter) {
223
delay = error.retryAfter * 1000;
224
}
225
226
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
227
await new Promise(resolve => setTimeout(resolve, delay));
228
}
229
}
230
231
throw lastError!;
232
}
233
234
// Usage
235
const asset = await withRetry(() =>
236
mux.video.assets.create({
237
inputs: [{ url: 'https://example.com/video.mp4' }],
238
})
239
);
240
```
241
242
### Validation Error Handling
243
244
```typescript
245
import { UnprocessableEntityError } from '@mux/mux-node';
246
247
async function handleValidationErrors() {
248
try {
249
const asset = await mux.video.assets.create({
250
inputs: [{ url: '' }], // Invalid empty URL
251
playback_policies: ['invalid-policy'], // Invalid policy
252
});
253
} catch (error) {
254
if (error instanceof UnprocessableEntityError) {
255
console.error('Validation errors occurred:');
256
257
// Parse validation errors from response body
258
const errorBody = error.error as any;
259
if (errorBody?.errors) {
260
for (const validationError of errorBody.errors) {
261
console.error(`Field: ${validationError.field}`);
262
console.error(`Message: ${validationError.message}`);
263
console.error(`Code: ${validationError.code}`);
264
}
265
}
266
}
267
}
268
}
269
```
270
271
### Upload Error Handling
272
273
```typescript
274
import { APIConnectionError, BadRequestError } from '@mux/mux-node';
275
276
async function handleUploadErrors() {
277
try {
278
const upload = await mux.video.uploads.create({
279
cors_origin: 'https://example.com',
280
});
281
282
// Monitor upload status with error handling
283
const checkUploadStatus = async (): Promise<string> => {
284
while (true) {
285
try {
286
const uploadStatus = await mux.video.uploads.retrieve(upload.id);
287
288
switch (uploadStatus.status) {
289
case 'asset_created':
290
return uploadStatus.asset_id!;
291
292
case 'errored':
293
throw new Error(`Upload failed: ${uploadStatus.error?.message}`);
294
295
case 'cancelled':
296
throw new Error('Upload was cancelled');
297
298
case 'timed_out':
299
throw new Error('Upload timed out');
300
301
case 'waiting':
302
await new Promise(resolve => setTimeout(resolve, 1000));
303
break;
304
}
305
} catch (error) {
306
if (error instanceof APIConnectionError) {
307
console.warn('Connection error checking upload status, retrying...');
308
await new Promise(resolve => setTimeout(resolve, 5000));
309
} else {
310
throw error;
311
}
312
}
313
}
314
};
315
316
const assetId = await checkUploadStatus();
317
console.log('Upload completed, asset created:', assetId);
318
319
} catch (error) {
320
if (error instanceof BadRequestError) {
321
console.error('Invalid upload parameters:', error.message);
322
} else {
323
console.error('Upload error:', error);
324
}
325
}
326
}
327
```
328
329
### Webhook Signature Verification Errors
330
331
```typescript
332
async function handleWebhookErrors(body: string, headers: Record<string, string>) {
333
try {
334
const event = mux.webhooks.unwrap(body, headers);
335
console.log('Webhook event:', event.type);
336
} catch (error) {
337
if (error instanceof MuxError) {
338
// Webhook signature verification failed
339
console.error('Invalid webhook signature');
340
// Return 400 status to webhook sender
341
return { status: 400, message: 'Invalid signature' };
342
} else {
343
console.error('Webhook processing error:', error);
344
// Return 500 status for processing errors
345
return { status: 500, message: 'Processing error' };
346
}
347
}
348
349
return { status: 200, message: 'OK' };
350
}
351
```
352
353
### Error Recovery Patterns
354
355
```typescript
356
import { AuthenticationError, NotFoundError } from '@mux/mux-node';
357
358
class MuxService {
359
private fallbackAssetId = 'default-asset-id';
360
361
async getAssetWithFallback(assetId: string) {
362
try {
363
return await mux.video.assets.retrieve(assetId);
364
} catch (error) {
365
if (error instanceof NotFoundError) {
366
console.warn(`Asset ${assetId} not found, using fallback`);
367
return await mux.video.assets.retrieve(this.fallbackAssetId);
368
} else if (error instanceof AuthenticationError) {
369
console.error('Authentication error - token may be expired');
370
await this.refreshAuthToken();
371
return await mux.video.assets.retrieve(assetId);
372
} else {
373
throw error;
374
}
375
}
376
}
377
378
private async refreshAuthToken() {
379
// Implement token refresh logic
380
console.log('Refreshing authentication token...');
381
}
382
}
383
```
384
385
### Error Logging and Monitoring
386
387
```typescript
388
import { APIError, APIConnectionError } from '@mux/mux-node';
389
390
function logError(error: Error, context?: Record<string, any>) {
391
const logData: Record<string, any> = {
392
timestamp: new Date().toISOString(),
393
error_type: error.constructor.name,
394
message: error.message,
395
stack: error.stack,
396
...context,
397
};
398
399
if (error instanceof APIError) {
400
logData.status = error.status;
401
logData.headers = error.headers;
402
logData.error_body = error.error;
403
}
404
405
if (error instanceof APIConnectionError) {
406
logData.network_error = true;
407
logData.cause = error.cause?.message;
408
}
409
410
// Send to logging service
411
console.error('Mux SDK Error:', JSON.stringify(logData, null, 2));
412
413
// Could also send to external monitoring service
414
// sendToMonitoring(logData);
415
}
416
417
// Usage in error handlers
418
try {
419
await mux.video.assets.create(params);
420
} catch (error) {
421
logError(error as Error, {
422
operation: 'asset_creation',
423
params: params,
424
user_id: 'user-123',
425
});
426
throw error;
427
}
428
```
429
430
## Error Prevention Best Practices
431
432
### Input Validation
433
434
```typescript
435
function validateAssetInput(input: any): string[] {
436
const errors: string[] = [];
437
438
if (!input.inputs || !Array.isArray(input.inputs) || input.inputs.length === 0) {
439
errors.push('At least one input is required');
440
}
441
442
for (const inputItem of input.inputs || []) {
443
if (!inputItem.url || typeof inputItem.url !== 'string') {
444
errors.push('Input URL is required and must be a string');
445
}
446
447
try {
448
new URL(inputItem.url);
449
} catch {
450
errors.push(`Invalid URL format: ${inputItem.url}`);
451
}
452
}
453
454
if (input.playback_policies) {
455
const validPolicies = ['public', 'signed', 'drm'];
456
for (const policy of input.playback_policies) {
457
if (!validPolicies.includes(policy)) {
458
errors.push(`Invalid playback policy: ${policy}`);
459
}
460
}
461
}
462
463
return errors;
464
}
465
466
async function createAssetSafely(input: any) {
467
const validationErrors = validateAssetInput(input);
468
if (validationErrors.length > 0) {
469
throw new Error(`Validation failed: ${validationErrors.join(', ')}`);
470
}
471
472
return await mux.video.assets.create(input);
473
}
474
```
475
476
## Types
477
478
```typescript { .api }
479
interface ErrorResponse {
480
/** Error code identifier */
481
code?: string;
482
/** Human-readable error message */
483
message: string;
484
/** Detailed error information */
485
details?: Record<string, any>;
486
/** Field-specific validation errors */
487
errors?: Array<ValidationError>;
488
}
489
490
interface ValidationError {
491
/** Field that failed validation */
492
field: string;
493
/** Validation error message */
494
message: string;
495
/** Validation error code */
496
code: string;
497
/** Invalid value */
498
rejected_value?: any;
499
}
500
501
interface RetryConfiguration {
502
/** Maximum number of retry attempts */
503
max_retries: number;
504
/** Initial delay in milliseconds */
505
initial_delay: number;
506
/** Maximum delay in milliseconds */
507
max_delay: number;
508
/** Backoff multiplier */
509
backoff_factor: number;
510
}
511
```