0
# Error Handling
1
2
Structured error classes for API interactions with status codes, response bodies, timeout handling, and HTTP-specific error types.
3
4
## Capabilities
5
6
### LangfuseAPIError
7
8
Base error class for all API-related errors with detailed context about the failed request.
9
10
```typescript { .api }
11
class LangfuseAPIError extends Error {
12
readonly statusCode?: number;
13
readonly body?: unknown;
14
readonly rawResponse?: RawResponse;
15
16
constructor(params: {
17
message?: string;
18
statusCode?: number;
19
body?: unknown;
20
rawResponse?: RawResponse;
21
});
22
}
23
24
interface RawResponse {
25
statusCode: number;
26
headers: Record<string, string>;
27
body: unknown;
28
}
29
```
30
31
**Import:**
32
33
```typescript
34
import { LangfuseAPIError } from '@langfuse/core';
35
```
36
37
#### Constructor
38
39
Creates a new API error with context about the failed request.
40
41
```typescript { .api }
42
constructor(params: {
43
message?: string;
44
statusCode?: number;
45
body?: unknown;
46
rawResponse?: RawResponse;
47
})
48
```
49
50
**Parameters:**
51
- `message` (optional) - Human-readable error message
52
- `statusCode` (optional) - HTTP status code (e.g., 400, 404, 500)
53
- `body` (optional) - Response body from the API (may contain error details)
54
- `rawResponse` (optional) - Complete raw HTTP response
55
56
**Properties:**
57
- `statusCode?: number` - HTTP status code of the failed request
58
- `body?: unknown` - Response body (often contains error details from API)
59
- `rawResponse?: RawResponse` - Complete raw response with headers and body
60
- `message: string` - Error message (inherited from Error)
61
- `name: string` - Error name "LangfuseAPIError" (inherited from Error)
62
- `stack?: string` - Stack trace (inherited from Error)
63
64
**Usage Examples:**
65
66
```typescript
67
import { LangfuseAPIClient, LangfuseAPIError } from '@langfuse/core';
68
69
const client = new LangfuseAPIClient({ /* ... */ });
70
71
try {
72
const trace = await client.trace.get('invalid-id');
73
} catch (error) {
74
if (error instanceof LangfuseAPIError) {
75
console.error('API Error:', error.message);
76
console.error('Status Code:', error.statusCode);
77
console.error('Response Body:', error.body);
78
79
// Access detailed error information
80
if (error.statusCode === 404) {
81
console.log('Trace not found');
82
} else if (error.statusCode === 429) {
83
console.log('Rate limited');
84
} else if (error.statusCode && error.statusCode >= 500) {
85
console.log('Server error');
86
}
87
88
// Log raw response for debugging
89
if (error.rawResponse) {
90
console.error('Raw Response:', {
91
status: error.rawResponse.statusCode,
92
headers: error.rawResponse.headers,
93
body: error.rawResponse.body
94
});
95
}
96
} else {
97
console.error('Unexpected error:', error);
98
}
99
}
100
```
101
102
### LangfuseAPITimeoutError
103
104
Error thrown when API requests exceed the configured timeout duration.
105
106
```typescript { .api }
107
class LangfuseAPITimeoutError extends Error {
108
constructor(message: string);
109
}
110
```
111
112
**Import:**
113
114
```typescript
115
import { LangfuseAPITimeoutError } from '@langfuse/core';
116
```
117
118
#### Constructor
119
120
Creates a new timeout error.
121
122
```typescript { .api }
123
constructor(message: string)
124
```
125
126
**Parameters:**
127
- `message` - Description of the timeout (includes duration and operation)
128
129
**Properties:**
130
- `message: string` - Error message describing the timeout
131
- `name: string` - Error name "LangfuseAPITimeoutError" (inherited from Error)
132
- `stack?: string` - Stack trace (inherited from Error)
133
134
**Usage Examples:**
135
136
```typescript
137
import {
138
LangfuseAPIClient,
139
LangfuseAPITimeoutError,
140
LangfuseAPIError
141
} from '@langfuse/core';
142
143
const client = new LangfuseAPIClient({ /* ... */ });
144
145
try {
146
const trace = await client.trace.get('trace-id', {
147
timeoutInSeconds: 5 // 5 second timeout
148
});
149
} catch (error) {
150
if (error instanceof LangfuseAPITimeoutError) {
151
console.error('Request timed out:', error.message);
152
// Implement retry logic or use cached data
153
} else if (error instanceof LangfuseAPIError) {
154
console.error('API error:', error.statusCode);
155
} else {
156
console.error('Unexpected error:', error);
157
}
158
}
159
```
160
161
### HTTP-Specific Error Classes
162
163
The API exports specific error classes for common HTTP status codes, all extending `LangfuseAPIError`.
164
165
```typescript { .api }
166
class Error extends LangfuseAPIError {
167
// Generic error (400 Bad Request)
168
}
169
170
class UnauthorizedError extends LangfuseAPIError {
171
// 401 Unauthorized - Invalid credentials
172
}
173
174
class AccessDeniedError extends LangfuseAPIError {
175
// 403 Forbidden - Insufficient permissions
176
}
177
178
class NotFoundError extends LangfuseAPIError {
179
// 404 Not Found - Resource doesn't exist
180
}
181
182
class MethodNotAllowedError extends LangfuseAPIError {
183
// 405 Method Not Allowed - HTTP method not supported
184
}
185
```
186
187
**Import:**
188
189
```typescript
190
import {
191
Error,
192
UnauthorizedError,
193
AccessDeniedError,
194
NotFoundError,
195
MethodNotAllowedError
196
} from '@langfuse/core';
197
```
198
199
**Note**: The generic `Error` class name may conflict with JavaScript's built-in Error. Consider importing with an alias:
200
201
```typescript
202
import { Error as LangfuseError } from '@langfuse/core';
203
```
204
205
**Usage Examples:**
206
207
```typescript
208
import {
209
LangfuseAPIClient,
210
UnauthorizedError,
211
AccessDeniedError,
212
NotFoundError
213
} from '@langfuse/core';
214
215
const client = new LangfuseAPIClient({ /* ... */ });
216
217
async function getTraceWithHandling(traceId: string) {
218
try {
219
return await client.trace.get(traceId);
220
} catch (error) {
221
if (error instanceof UnauthorizedError) {
222
console.error('Invalid API credentials');
223
// Refresh credentials or prompt user to login
224
throw new Error('Please check your API keys');
225
}
226
227
if (error instanceof AccessDeniedError) {
228
console.error('Insufficient permissions to access trace');
229
// Request additional permissions
230
throw new Error('You do not have permission to view this trace');
231
}
232
233
if (error instanceof NotFoundError) {
234
console.log('Trace not found, returning null');
235
return null; // Handle gracefully
236
}
237
238
// Rethrow other errors
239
throw error;
240
}
241
}
242
```
243
244
## Usage Patterns
245
246
### Comprehensive Error Handling
247
248
```typescript
249
import {
250
LangfuseAPIClient,
251
LangfuseAPIError,
252
LangfuseAPITimeoutError,
253
UnauthorizedError,
254
AccessDeniedError,
255
NotFoundError
256
} from '@langfuse/core';
257
258
async function robustAPICall<T>(
259
operation: () => Promise<T>,
260
fallback?: T
261
): Promise<T | undefined> {
262
try {
263
return await operation();
264
} catch (error) {
265
// Handle specific error types
266
if (error instanceof LangfuseAPITimeoutError) {
267
console.warn('Request timed out, using fallback');
268
return fallback;
269
}
270
271
if (error instanceof UnauthorizedError) {
272
console.error('Authentication failed');
273
throw new Error('Please check your API credentials');
274
}
275
276
if (error instanceof AccessDeniedError) {
277
console.error('Access denied');
278
throw new Error('Insufficient permissions');
279
}
280
281
if (error instanceof NotFoundError) {
282
console.log('Resource not found, using fallback');
283
return fallback;
284
}
285
286
if (error instanceof LangfuseAPIError) {
287
// Generic API error
288
console.error(`API Error [${error.statusCode}]:`, error.message);
289
290
if (error.statusCode === 429) {
291
console.warn('Rate limited, please retry later');
292
throw new Error('Rate limit exceeded');
293
}
294
295
if (error.statusCode && error.statusCode >= 500) {
296
console.error('Server error, using fallback');
297
return fallback;
298
}
299
300
console.error('Response body:', error.body);
301
throw error;
302
}
303
304
// Unknown error
305
console.error('Unexpected error:', error);
306
throw error;
307
}
308
}
309
310
// Usage
311
const client = new LangfuseAPIClient({ /* ... */ });
312
313
const trace = await robustAPICall(
314
() => client.trace.get('trace-id'),
315
null // Fallback value
316
);
317
```
318
319
### Retry with Exponential Backoff
320
321
```typescript
322
import {
323
LangfuseAPIClient,
324
LangfuseAPIError,
325
LangfuseAPITimeoutError
326
} from '@langfuse/core';
327
328
async function retryWithBackoff<T>(
329
operation: () => Promise<T>,
330
maxRetries: number = 3,
331
initialDelay: number = 1000
332
): Promise<T> {
333
let lastError: Error | undefined;
334
335
for (let attempt = 0; attempt < maxRetries; attempt++) {
336
try {
337
return await operation();
338
} catch (error) {
339
lastError = error as Error;
340
341
// Don't retry on client errors (4xx)
342
if (error instanceof LangfuseAPIError) {
343
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
344
// Don't retry 4xx errors (except 429 rate limit)
345
if (error.statusCode !== 429) {
346
throw error;
347
}
348
}
349
}
350
351
// Don't retry on final attempt
352
if (attempt === maxRetries - 1) {
353
break;
354
}
355
356
// Calculate delay with exponential backoff
357
const delay = initialDelay * Math.pow(2, attempt);
358
console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
359
360
await new Promise(resolve => setTimeout(resolve, delay));
361
}
362
}
363
364
throw lastError!;
365
}
366
367
// Usage
368
const client = new LangfuseAPIClient({ /* ... */ });
369
370
const trace = await retryWithBackoff(
371
() => client.trace.get('trace-id'),
372
5, // Max 5 attempts
373
1000 // Start with 1 second delay
374
);
375
```
376
377
### Error Logging and Monitoring
378
379
```typescript
380
import {
381
LangfuseAPIClient,
382
LangfuseAPIError,
383
LangfuseAPITimeoutError,
384
getGlobalLogger
385
} from '@langfuse/core';
386
387
const logger = getGlobalLogger();
388
389
async function monitoredAPICall<T>(
390
operationName: string,
391
operation: () => Promise<T>
392
): Promise<T> {
393
const startTime = Date.now();
394
395
try {
396
logger.debug(`Starting ${operationName}`);
397
const result = await operation();
398
const duration = Date.now() - startTime;
399
logger.info(`${operationName} completed in ${duration}ms`);
400
return result;
401
} catch (error) {
402
const duration = Date.now() - startTime;
403
404
if (error instanceof LangfuseAPITimeoutError) {
405
logger.error(`${operationName} timed out after ${duration}ms`);
406
// Send to monitoring service
407
sendToMonitoring({
408
operation: operationName,
409
error: 'timeout',
410
duration
411
});
412
} else if (error instanceof LangfuseAPIError) {
413
logger.error(
414
`${operationName} failed with status ${error.statusCode}`,
415
error.body
416
);
417
// Send to monitoring service
418
sendToMonitoring({
419
operation: operationName,
420
error: 'api_error',
421
statusCode: error.statusCode,
422
duration
423
});
424
} else {
425
logger.error(`${operationName} failed with unexpected error`, error);
426
// Send to monitoring service
427
sendToMonitoring({
428
operation: operationName,
429
error: 'unexpected',
430
duration
431
});
432
}
433
434
throw error;
435
}
436
}
437
438
// Usage
439
const trace = await monitoredAPICall(
440
'fetch_trace',
441
() => client.trace.get('trace-id')
442
);
443
```
444
445
### Circuit Breaker Pattern
446
447
```typescript
448
import { LangfuseAPIClient, LangfuseAPIError } from '@langfuse/core';
449
450
class CircuitBreaker {
451
private failureCount = 0;
452
private lastFailureTime = 0;
453
private state: 'closed' | 'open' | 'half-open' = 'closed';
454
455
constructor(
456
private threshold: number = 5,
457
private timeout: number = 60000
458
) {}
459
460
async execute<T>(operation: () => Promise<T>): Promise<T> {
461
if (this.state === 'open') {
462
if (Date.now() - this.lastFailureTime > this.timeout) {
463
this.state = 'half-open';
464
this.failureCount = 0;
465
} else {
466
throw new Error('Circuit breaker is open');
467
}
468
}
469
470
try {
471
const result = await operation();
472
473
if (this.state === 'half-open') {
474
this.state = 'closed';
475
}
476
477
this.failureCount = 0;
478
return result;
479
} catch (error) {
480
this.failureCount++;
481
this.lastFailureTime = Date.now();
482
483
if (this.failureCount >= this.threshold) {
484
this.state = 'open';
485
console.error('Circuit breaker opened due to repeated failures');
486
}
487
488
throw error;
489
}
490
}
491
}
492
493
// Usage
494
const client = new LangfuseAPIClient({ /* ... */ });
495
const breaker = new CircuitBreaker(5, 60000);
496
497
async function getTraceWithCircuitBreaker(traceId: string) {
498
return breaker.execute(() => client.trace.get(traceId));
499
}
500
```
501
502
### User-Friendly Error Messages
503
504
```typescript
505
import {
506
LangfuseAPIError,
507
UnauthorizedError,
508
AccessDeniedError,
509
NotFoundError,
510
LangfuseAPITimeoutError
511
} from '@langfuse/core';
512
513
function getUserFriendlyMessage(error: unknown): string {
514
if (error instanceof UnauthorizedError) {
515
return 'Your API credentials are invalid. Please check your configuration.';
516
}
517
518
if (error instanceof AccessDeniedError) {
519
return 'You do not have permission to perform this action. Please contact your administrator.';
520
}
521
522
if (error instanceof NotFoundError) {
523
return 'The requested resource was not found.';
524
}
525
526
if (error instanceof LangfuseAPITimeoutError) {
527
return 'The request took too long to complete. Please try again.';
528
}
529
530
if (error instanceof LangfuseAPIError) {
531
if (error.statusCode === 429) {
532
return 'Too many requests. Please wait a moment and try again.';
533
}
534
535
if (error.statusCode && error.statusCode >= 500) {
536
return 'The Langfuse service is temporarily unavailable. Please try again later.';
537
}
538
539
return `An API error occurred (${error.statusCode}). Please try again.`;
540
}
541
542
return 'An unexpected error occurred. Please try again.';
543
}
544
545
// Usage in UI
546
try {
547
await client.trace.get('trace-id');
548
} catch (error) {
549
const message = getUserFriendlyMessage(error);
550
showErrorToUser(message);
551
}
552
```
553
554
## Type Definitions
555
556
```typescript { .api }
557
class LangfuseAPIError extends Error {
558
readonly statusCode?: number;
559
readonly body?: unknown;
560
readonly rawResponse?: RawResponse;
561
562
constructor(params: {
563
message?: string;
564
statusCode?: number;
565
body?: unknown;
566
rawResponse?: RawResponse;
567
});
568
}
569
570
class LangfuseAPITimeoutError extends Error {
571
constructor(message: string);
572
}
573
574
class Error extends LangfuseAPIError {}
575
class UnauthorizedError extends LangfuseAPIError {}
576
class AccessDeniedError extends LangfuseAPIError {}
577
class NotFoundError extends LangfuseAPIError {}
578
class MethodNotAllowedError extends LangfuseAPIError {}
579
580
interface RawResponse {
581
statusCode: number;
582
headers: Record<string, string>;
583
body: unknown;
584
}
585
```
586
587
## Best Practices
588
589
1. **Specific Error Handling**: Always check for specific error types before handling generic `LangfuseAPIError`
590
2. **Timeout Configuration**: Set appropriate timeouts based on operation type (longer for batch operations)
591
3. **Retry Logic**: Implement exponential backoff for transient errors (timeouts, 5xx, 429)
592
4. **Don't Retry 4xx**: Never retry client errors (4xx) except for 429 rate limiting
593
5. **Log Error Details**: Always log `statusCode` and `body` for debugging
594
6. **User-Friendly Messages**: Transform technical errors into user-friendly messages in UI
595
7. **Circuit Breaker**: Use circuit breaker pattern for external service calls
596
8. **Monitoring**: Send error metrics to monitoring services for alerting
597
9. **Graceful Degradation**: Provide fallback values or cached data when possible
598
10. **Error Context**: Include operation context in error logs (operation name, parameters, duration)
599
600
## Common HTTP Status Codes
601
602
- **400 Bad Request**: Invalid request parameters or body
603
- **401 Unauthorized**: Missing or invalid API credentials
604
- **403 Forbidden**: Valid credentials but insufficient permissions
605
- **404 Not Found**: Resource doesn't exist
606
- **405 Method Not Allowed**: HTTP method not supported for endpoint
607
- **429 Too Many Requests**: Rate limit exceeded (implement backoff and retry)
608
- **500 Internal Server Error**: Server-side error (safe to retry)
609
- **502 Bad Gateway**: Upstream service error (safe to retry)
610
- **503 Service Unavailable**: Service temporarily down (safe to retry)
611
- **504 Gateway Timeout**: Upstream service timeout (safe to retry)
612