0
# Error Handling
1
2
Vue's error handling system provides utilities for graceful error management, including error capture, handling, and reporting mechanisms for building robust applications.
3
4
## Capabilities
5
6
### Error Handling Functions
7
8
Handle errors with proper context and error information.
9
10
```typescript { .api }
11
/**
12
* Handles errors with Vue's error handling pipeline
13
* @param err - Error to handle
14
* @param instance - Component instance where error occurred
15
* @param type - Error type for categorization
16
*/
17
function handleError(
18
err: unknown,
19
instance: ComponentInternalInstance | null,
20
type: ErrorTypes
21
): void;
22
23
/**
24
* Calls a function with error handling
25
* @param fn - Function to call
26
* @param instance - Component instance context
27
* @param type - Error type
28
* @param args - Arguments to pass to function
29
* @returns Function result or undefined on error
30
*/
31
function callWithErrorHandling<T extends any[], R>(
32
fn: (...args: T) => R,
33
instance: ComponentInternalInstance | null,
34
type: ErrorTypes,
35
args?: T
36
): R | undefined;
37
38
/**
39
* Calls async function(s) with error handling
40
* @param fn - Function or array of functions to call
41
* @param instance - Component instance context
42
* @param type - Error type
43
* @param args - Arguments to pass to function(s)
44
* @returns Promise resolving to results
45
*/
46
function callWithAsyncErrorHandling<T extends any[], R>(
47
fn: Function | Function[],
48
instance: ComponentInternalInstance | null,
49
type: ErrorTypes,
50
args?: T
51
): Promise<R[]>;
52
```
53
54
**Usage Examples:**
55
56
```typescript
57
import {
58
defineComponent,
59
handleError,
60
callWithErrorHandling,
61
callWithAsyncErrorHandling,
62
ErrorCodes
63
} from "@vue/runtime-core";
64
65
// Basic error handling
66
const SafeComponent = defineComponent({
67
setup() {
68
const riskyOperation = () => {
69
try {
70
// Some operation that might throw
71
JSON.parse('invalid json');
72
} catch (error) {
73
handleError(error, getCurrentInstance(), ErrorCodes.SETUP_FUNCTION);
74
}
75
};
76
77
// Safe function call with error handling
78
const safeFunctionCall = (userFunction: Function) => {
79
const result = callWithErrorHandling(
80
userFunction,
81
getCurrentInstance(),
82
ErrorCodes.COMPONENT_EVENT_HANDLER
83
);
84
85
console.log('Function result:', result);
86
};
87
88
return {
89
riskyOperation,
90
safeFunctionCall
91
};
92
}
93
});
94
95
// Async error handling
96
const AsyncErrorComponent = defineComponent({
97
setup() {
98
const handleAsyncOperations = async () => {
99
const asyncFunctions = [
100
async () => {
101
await fetch('/api/data1');
102
return 'data1';
103
},
104
async () => {
105
await fetch('/api/data2');
106
return 'data2';
107
},
108
async () => {
109
throw new Error('Intentional error');
110
}
111
];
112
113
const results = await callWithAsyncErrorHandling(
114
asyncFunctions,
115
getCurrentInstance(),
116
ErrorCodes.COMPONENT_EVENT_HANDLER
117
);
118
119
console.log('Async results:', results);
120
};
121
122
return {
123
handleAsyncOperations
124
};
125
}
126
});
127
128
// Error handling in lifecycle hooks
129
const LifecycleErrorComponent = defineComponent({
130
setup() {
131
onMounted(() => {
132
const initializeThirdPartyLib = () => {
133
// Third-party library initialization that might fail
134
window.ThirdPartyLib?.init({
135
apiKey: 'invalid-key'
136
});
137
};
138
139
callWithErrorHandling(
140
initializeThirdPartyLib,
141
getCurrentInstance(),
142
ErrorCodes.COMPONENT_EVENT_HANDLER
143
);
144
});
145
146
return {};
147
}
148
});
149
```
150
151
### Error Codes and Types
152
153
Categorize errors for better handling and debugging.
154
155
```typescript { .api }
156
/**
157
* Enumeration of error codes for different error types
158
*/
159
enum ErrorCodes {
160
SETUP_FUNCTION = 'setup function',
161
RENDER_FUNCTION = 'render function',
162
WATCH_GETTER = 'watcher getter',
163
WATCH_CALLBACK = 'watcher callback',
164
WATCH_CLEANUP = 'watcher cleanup function',
165
NATIVE_EVENT_HANDLER = 'native event handler',
166
COMPONENT_EVENT_HANDLER = 'component event handler',
167
VNODE_HOOK = 'vnode hook',
168
DIRECTIVE_HOOK = 'directive hook',
169
TRANSITION_HOOK = 'transition hook',
170
APP_ERROR_HANDLER = 'app errorHandler',
171
APP_WARN_HANDLER = 'app warnHandler',
172
FUNCTION_REF = 'ref function',
173
ASYNC_COMPONENT_LOADER = 'async component loader',
174
SCHEDULER = 'scheduler flush'
175
}
176
177
type ErrorTypes = ErrorCodes | string;
178
```
179
180
**Usage Examples:**
181
182
```typescript
183
// Custom error handling by type
184
const createErrorHandler = () => {
185
const errorCounts = reactive<Record<string, number>>({});
186
187
const customHandleError = (
188
err: unknown,
189
instance: ComponentInternalInstance | null,
190
type: ErrorTypes
191
) => {
192
// Track error frequency
193
if (typeof type === 'string') {
194
errorCounts[type] = (errorCounts[type] || 0) + 1;
195
}
196
197
// Different handling based on error type
198
switch (type) {
199
case ErrorCodes.RENDER_FUNCTION:
200
console.error('Render error - component may be broken:', err);
201
break;
202
203
case ErrorCodes.ASYNC_COMPONENT_LOADER:
204
console.warn('Async component failed to load:', err);
205
break;
206
207
case ErrorCodes.NATIVE_EVENT_HANDLER:
208
console.error('Native event handler error:', err);
209
break;
210
211
default:
212
console.error(`Error in ${type}:`, err);
213
}
214
215
// Use Vue's default error handling
216
handleError(err, instance, type);
217
};
218
219
return {
220
customHandleError,
221
errorCounts: readonly(errorCounts)
222
};
223
};
224
225
// Error boundary component
226
const ErrorBoundary = defineComponent({
227
setup(_, { slots }) {
228
const hasError = ref(false);
229
const error = ref<Error | null>(null);
230
231
onErrorCaptured((err, instance, info) => {
232
hasError.value = true;
233
error.value = err instanceof Error ? err : new Error(String(err));
234
235
// Log error with context
236
handleError(err, instance, `error boundary: ${info}`);
237
238
// Prevent error from propagating
239
return false;
240
});
241
242
const retry = () => {
243
hasError.value = false;
244
error.value = null;
245
};
246
247
return () => {
248
if (hasError.value) {
249
return h('div', { class: 'error-boundary' }, [
250
h('h3', 'Something went wrong'),
251
h('p', error.value?.message || 'Unknown error'),
252
h('button', { onClick: retry }, 'Retry')
253
]);
254
}
255
256
return slots.default?.();
257
};
258
}
259
});
260
```
261
262
### Development Tools Integration
263
264
Warning and development utilities for debugging.
265
266
```typescript { .api }
267
/**
268
* Development warning utility (only in development mode)
269
* @param msg - Warning message
270
* @param args - Additional arguments to log
271
*/
272
declare const warn: (msg: string, ...args: any[]) => void;
273
274
/**
275
* Assert that a value is a number (development only)
276
* @param val - Value to check
277
* @param type - Context type for error message
278
*/
279
function assertNumber(val: unknown, type: string): void;
280
```
281
282
**Usage Examples:**
283
284
```typescript
285
import { warn, assertNumber } from "@vue/runtime-core";
286
287
const ValidationComponent = defineComponent({
288
props: {
289
count: [Number, String],
290
timeout: Number
291
},
292
293
setup(props) {
294
// Development warnings
295
if (__DEV__) {
296
if (typeof props.count === 'string') {
297
warn('count prop should be a number, received string:', props.count);
298
}
299
300
// Assert number type
301
if (props.timeout !== undefined) {
302
assertNumber(props.timeout, 'timeout prop');
303
}
304
}
305
306
const processCount = () => {
307
const numericCount = typeof props.count === 'string'
308
? parseInt(props.count, 10)
309
: props.count || 0;
310
311
if (__DEV__ && isNaN(numericCount)) {
312
warn('Invalid count value, defaulting to 0');
313
}
314
315
return isNaN(numericCount) ? 0 : numericCount;
316
};
317
318
return {
319
processCount
320
};
321
}
322
});
323
324
// Custom validation with warnings
325
const useValidation = () => {
326
const validateProps = (props: Record<string, any>, schema: Record<string, any>) => {
327
for (const [key, validator] of Object.entries(schema)) {
328
const value = props[key];
329
330
if (validator.required && (value === undefined || value === null)) {
331
if (__DEV__) {
332
warn(`Required prop "${key}" is missing`);
333
}
334
continue;
335
}
336
337
if (value !== undefined && validator.type && typeof value !== validator.type) {
338
if (__DEV__) {
339
warn(`Prop "${key}" expected ${validator.type}, got ${typeof value}`);
340
}
341
}
342
343
if (validator.validator && !validator.validator(value)) {
344
if (__DEV__) {
345
warn(`Prop "${key}" failed custom validation`);
346
}
347
}
348
}
349
};
350
351
return { validateProps };
352
};
353
```
354
355
### Advanced Error Handling Patterns
356
357
```typescript
358
// Global error handler setup
359
const setupGlobalErrorHandling = (app: App) => {
360
app.config.errorHandler = (err, instance, info) => {
361
// Custom global error handling
362
console.error('Global error:', err);
363
console.log('Component:', instance);
364
console.log('Error info:', info);
365
366
// Send to error reporting service
367
sendErrorToService(err, {
368
component: instance?.type?.name || 'Unknown',
369
info,
370
stack: err instanceof Error ? err.stack : undefined
371
});
372
373
// Use Vue's error handling
374
handleError(err, instance, `global handler: ${info}`);
375
};
376
377
app.config.warnHandler = (msg, instance, trace) => {
378
console.warn('Vue warning:', msg);
379
if (instance) {
380
console.log('Component:', instance.type?.name);
381
}
382
console.log('Trace:', trace);
383
};
384
};
385
386
// Error recovery utilities
387
const useErrorRecovery = () => {
388
const retryCount = ref(0);
389
const maxRetries = 3;
390
391
const withRetry = async <T>(
392
operation: () => Promise<T>,
393
errorType: ErrorTypes = ErrorCodes.COMPONENT_EVENT_HANDLER
394
): Promise<T | null> => {
395
try {
396
const result = await callWithAsyncErrorHandling(
397
[operation],
398
getCurrentInstance(),
399
errorType
400
);
401
402
retryCount.value = 0; // Reset on success
403
return result[0];
404
405
} catch (error) {
406
retryCount.value++;
407
408
if (retryCount.value < maxRetries) {
409
console.log(`Retrying operation (${retryCount.value}/${maxRetries})`);
410
return withRetry(operation, errorType);
411
}
412
413
console.error('Max retries exceeded');
414
return null;
415
}
416
};
417
418
return {
419
withRetry,
420
retryCount: readonly(retryCount)
421
};
422
};
423
424
// Circuit breaker pattern
425
class CircuitBreaker {
426
private failures = 0;
427
private lastFailureTime = 0;
428
private state: 'closed' | 'open' | 'half-open' = 'closed';
429
430
constructor(
431
private failureThreshold = 5,
432
private recoveryTimeout = 30000
433
) {}
434
435
async execute<T>(
436
operation: () => Promise<T>,
437
fallback?: () => T
438
): Promise<T> {
439
if (this.state === 'open') {
440
if (Date.now() - this.lastFailureTime > this.recoveryTimeout) {
441
this.state = 'half-open';
442
} else {
443
if (fallback) return fallback();
444
throw new Error('Circuit breaker is open');
445
}
446
}
447
448
try {
449
const result = await callWithAsyncErrorHandling(
450
[operation],
451
null,
452
ErrorCodes.COMPONENT_EVENT_HANDLER
453
);
454
455
// Success - reset circuit breaker
456
if (this.state === 'half-open') {
457
this.state = 'closed';
458
this.failures = 0;
459
}
460
461
return result[0];
462
463
} catch (error) {
464
this.failures++;
465
this.lastFailureTime = Date.now();
466
467
if (this.failures >= this.failureThreshold) {
468
this.state = 'open';
469
}
470
471
if (fallback) return fallback();
472
throw error;
473
}
474
}
475
}
476
```
477
478
## Types
479
480
```typescript { .api }
481
enum ErrorCodes {
482
SETUP_FUNCTION = 'setup function',
483
RENDER_FUNCTION = 'render function',
484
WATCH_GETTER = 'watcher getter',
485
WATCH_CALLBACK = 'watcher callback',
486
WATCH_CLEANUP = 'watcher cleanup function',
487
NATIVE_EVENT_HANDLER = 'native event handler',
488
COMPONENT_EVENT_HANDLER = 'component event handler',
489
VNODE_HOOK = 'vnode hook',
490
DIRECTIVE_HOOK = 'directive hook',
491
TRANSITION_HOOK = 'transition hook',
492
APP_ERROR_HANDLER = 'app errorHandler',
493
APP_WARN_HANDLER = 'app warnHandler',
494
FUNCTION_REF = 'ref function',
495
ASYNC_COMPONENT_LOADER = 'async component loader',
496
SCHEDULER = 'scheduler flush'
497
}
498
499
type ErrorTypes = ErrorCodes | string;
500
501
interface ErrorHandler {
502
(err: unknown, instance: ComponentInternalInstance | null, info: string): void;
503
}
504
505
interface WarnHandler {
506
(msg: string, instance: ComponentInternalInstance | null, trace: string): void;
507
}
508
```
509
510
## Best Practices
511
512
### Error Handling Strategy
513
514
1. **Graceful Degradation**: Always provide fallbacks for failed operations
515
2. **Error Boundaries**: Use `onErrorCaptured` to contain errors within components
516
3. **Contextual Logging**: Include component and operation context in error reports
517
4. **User-Friendly Messages**: Don't expose technical errors directly to users
518
519
### Development vs Production
520
521
```typescript
522
// Development-only error handling
523
if (__DEV__) {
524
warn('This is a development warning');
525
assertNumber(value, 'prop name');
526
}
527
528
// Production error handling
529
const handleProductionError = (error: unknown) => {
530
// Log to service, show user-friendly message
531
console.error('An error occurred');
532
// Don't expose internal details
533
};
534
```
535
536
### Error Recovery Patterns
537
538
1. **Retry Logic**: Implement retry mechanisms for transient failures
539
2. **Circuit Breakers**: Prevent cascading failures in distributed systems
540
3. **Fallback Content**: Always have backup content for failed async operations
541
4. **Error Boundaries**: Isolate errors to prevent full application crashes