0
# Error Handling
1
2
Comprehensive error hierarchy for handling database-specific errors, validation failures, and connection issues.
3
4
## Capabilities
5
6
### Base Error Classes
7
8
Foundation error classes that other Sequelize errors extend from.
9
10
```typescript { .api }
11
/**
12
* Base error class for all Sequelize errors
13
*/
14
class BaseError extends Error {
15
/** Error name */
16
name: string;
17
/** Error message */
18
message: string;
19
/** Stack trace */
20
stack?: string;
21
}
22
23
/**
24
* Database-related errors with SQL context
25
*/
26
class DatabaseError extends BaseError {
27
/** SQL query that caused the error */
28
sql?: string;
29
/** Query parameters */
30
parameters?: any[];
31
/** Original database error */
32
original?: Error;
33
/** Error number from database */
34
errno?: number;
35
/** SQL state code */
36
sqlState?: string;
37
/** SQL message from database */
38
sqlMessage?: string;
39
}
40
```
41
42
### Validation Errors
43
44
Errors related to model validation and data integrity.
45
46
```typescript { .api }
47
/**
48
* Model validation error containing multiple validation failures
49
*/
50
class ValidationError extends BaseError {
51
/** Array of individual validation errors */
52
errors: ValidationErrorItem[];
53
/** Fields that failed validation */
54
fields: { [field: string]: ValidationErrorItem[] };
55
}
56
57
/**
58
* Individual validation error for a specific field
59
*/
60
class ValidationErrorItem {
61
/** Error message */
62
message: string;
63
/** Validation type */
64
type: string;
65
/** Field path */
66
path: string;
67
/** Field value that failed */
68
value: any;
69
/** Validator origin */
70
origin: string;
71
/** Model instance */
72
instance?: Model;
73
/** Validator key */
74
validatorKey?: string;
75
/** Validator name */
76
validatorName?: string;
77
/** Validator arguments */
78
validatorArgs?: any[];
79
}
80
81
/**
82
* Unique constraint violation error
83
*/
84
class UniqueConstraintError extends ValidationError {
85
/** Fields that caused the constraint violation */
86
fields: string[];
87
/** Values that caused the violation */
88
value: any;
89
/** Index name */
90
index: string;
91
/** Relation name */
92
reltype?: string;
93
}
94
95
/**
96
* Foreign key constraint violation error
97
*/
98
class ForeignKeyConstraintError extends DatabaseError {
99
/** Referenced table */
100
table: string;
101
/** Referenced fields */
102
fields: string[];
103
/** Referenced value */
104
value: any;
105
/** Constraint index */
106
index: string;
107
/** Relation type */
108
reltype?: string;
109
}
110
111
/**
112
* Exclusion constraint violation error
113
*/
114
class ExclusionConstraintError extends DatabaseError {
115
/** Constraint fields */
116
fields: string[];
117
/** Constraint table */
118
table: string;
119
/** Constraint index */
120
constraint: string;
121
}
122
```
123
124
**Usage Examples:**
125
126
```typescript
127
try {
128
await User.create({
129
email: 'invalid-email',
130
age: -5
131
});
132
} catch (error) {
133
if (error instanceof ValidationError) {
134
console.log('Validation failed:');
135
error.errors.forEach(err => {
136
console.log(`- ${err.path}: ${err.message}`);
137
});
138
139
// Check specific field errors
140
if (error.fields.email) {
141
console.log('Email validation failed');
142
}
143
}
144
}
145
146
// Handle unique constraint violations
147
try {
148
await User.create({
149
email: 'existing@example.com' // Already exists
150
});
151
} catch (error) {
152
if (error instanceof UniqueConstraintError) {
153
console.log(`Duplicate value for fields: ${error.fields.join(', ')}`);
154
console.log(`Constraint: ${error.index}`);
155
}
156
}
157
158
// Handle foreign key violations
159
try {
160
await Post.create({
161
title: 'My Post',
162
userId: 999 // Non-existent user
163
});
164
} catch (error) {
165
if (error instanceof ForeignKeyConstraintError) {
166
console.log(`Foreign key violation on table: ${error.table}`);
167
console.log(`Fields: ${error.fields.join(', ')}`);
168
}
169
}
170
```
171
172
### Connection Errors
173
174
Errors related to database connectivity and access.
175
176
```typescript { .api }
177
/**
178
* Base connection error
179
*/
180
class ConnectionError extends BaseError {
181
/** Original connection error */
182
original?: Error;
183
}
184
185
/**
186
* Database access denied
187
*/
188
class AccessDeniedError extends ConnectionError {}
189
190
/**
191
* Connection acquisition timeout
192
*/
193
class ConnectionAcquireTimeoutError extends ConnectionError {}
194
195
/**
196
* Connection refused by database
197
*/
198
class ConnectionRefusedError extends ConnectionError {}
199
200
/**
201
* Connection timed out
202
*/
203
class ConnectionTimedOutError extends ConnectionError {}
204
205
/**
206
* Database host not found
207
*/
208
class HostNotFoundError extends ConnectionError {}
209
210
/**
211
* Database host not reachable
212
*/
213
class HostNotReachableError extends ConnectionError {}
214
215
/**
216
* Invalid connection configuration
217
*/
218
class InvalidConnectionError extends ConnectionError {}
219
220
/**
221
* Database timeout error
222
*/
223
class TimeoutError extends DatabaseError {}
224
```
225
226
**Usage Examples:**
227
228
```typescript
229
try {
230
await sequelize.authenticate();
231
} catch (error) {
232
if (error instanceof ConnectionRefusedError) {
233
console.log('Database refused connection - check if database is running');
234
} else if (error instanceof AccessDeniedError) {
235
console.log('Access denied - check credentials');
236
} else if (error instanceof HostNotFoundError) {
237
console.log('Database host not found - check hostname');
238
} else if (error instanceof ConnectionTimedOutError) {
239
console.log('Connection timed out - check network or increase timeout');
240
}
241
}
242
243
// Handle query timeouts
244
try {
245
await User.findAll({
246
// Long-running query
247
});
248
} catch (error) {
249
if (error instanceof TimeoutError) {
250
console.log('Query timed out - consider optimizing or increasing timeout');
251
}
252
}
253
```
254
255
### Application Logic Errors
256
257
Errors related to application logic and ORM usage.
258
259
```typescript { .api }
260
/**
261
* Model instance-related error
262
*/
263
class InstanceError extends BaseError {}
264
265
/**
266
* Association-related error
267
*/
268
class AssociationError extends BaseError {}
269
270
/**
271
* Query-related error
272
*/
273
class QueryError extends BaseError {
274
/** SQL query */
275
sql?: string;
276
/** Query parameters */
277
parameters?: any[];
278
}
279
280
/**
281
* Scope-related error
282
*/
283
class SequelizeScopeError extends BaseError {}
284
285
/**
286
* Eager loading error
287
*/
288
class EagerLoadingError extends BaseError {}
289
290
/**
291
* Empty result error (when expecting results)
292
*/
293
class EmptyResultError extends BaseError {}
294
295
/**
296
* Optimistic locking error
297
*/
298
class OptimisticLockError extends BaseError {}
299
300
/**
301
* Bulk operation error
302
*/
303
class BulkRecordError extends BaseError {
304
/** Records that failed */
305
errors: Error[];
306
}
307
308
/**
309
* Multiple errors aggregated
310
*/
311
class AggregateError extends BaseError {
312
/** Individual errors */
313
errors: Error[];
314
}
315
```
316
317
**Usage Examples:**
318
319
```typescript
320
// Handle empty results
321
try {
322
const user = await User.findOne({
323
where: { id: 999 },
324
rejectOnEmpty: true // Throws EmptyResultError if not found
325
});
326
} catch (error) {
327
if (error instanceof EmptyResultError) {
328
console.log('User not found');
329
}
330
}
331
332
// Handle optimistic locking
333
try {
334
const user = await User.findByPk(1);
335
user.name = 'Updated';
336
await user.save(); // May fail if record was modified
337
} catch (error) {
338
if (error instanceof OptimisticLockError) {
339
console.log('Record was modified by another process');
340
// Reload and retry
341
}
342
}
343
344
// Handle bulk operation errors
345
try {
346
await User.bulkCreate([
347
{ email: 'user1@example.com' },
348
{ email: 'invalid-email' }, // Will fail validation
349
{ email: 'user3@example.com' }
350
]);
351
} catch (error) {
352
if (error instanceof BulkRecordError) {
353
console.log(`${error.errors.length} records failed`);
354
error.errors.forEach((err, index) => {
355
console.log(`Record ${index}: ${err.message}`);
356
});
357
}
358
}
359
```
360
361
### Error Handling Patterns
362
363
Common patterns for handling Sequelize errors in applications.
364
365
```typescript { .api }
366
// Error type guards
367
function isSequelizeError(error: any): error is BaseError {
368
return error instanceof BaseError;
369
}
370
371
function isValidationError(error: any): error is ValidationError {
372
return error instanceof ValidationError;
373
}
374
375
function isConnectionError(error: any): error is ConnectionError {
376
return error instanceof ConnectionError;
377
}
378
```
379
380
**Usage Examples:**
381
382
```typescript
383
// Comprehensive error handling middleware (Express.js example)
384
function sequelizeErrorHandler(error: any, req: any, res: any, next: any) {
385
if (!isSequelizeError(error)) {
386
return next(error);
387
}
388
389
// Validation errors
390
if (error instanceof ValidationError) {
391
return res.status(400).json({
392
type: 'validation_error',
393
message: 'Validation failed',
394
errors: error.errors.map(err => ({
395
field: err.path,
396
message: err.message,
397
value: err.value
398
}))
399
});
400
}
401
402
// Unique constraint violations
403
if (error instanceof UniqueConstraintError) {
404
return res.status(409).json({
405
type: 'duplicate_error',
406
message: `Duplicate value for: ${error.fields.join(', ')}`,
407
fields: error.fields
408
});
409
}
410
411
// Foreign key violations
412
if (error instanceof ForeignKeyConstraintError) {
413
return res.status(400).json({
414
type: 'reference_error',
415
message: 'Invalid reference to related record',
416
table: error.table,
417
fields: error.fields
418
});
419
}
420
421
// Connection errors
422
if (isConnectionError(error)) {
423
console.error('Database connection error:', error);
424
return res.status(503).json({
425
type: 'database_error',
426
message: 'Database temporarily unavailable'
427
});
428
}
429
430
// Other database errors
431
if (error instanceof DatabaseError) {
432
console.error('Database error:', error.sql, error.parameters);
433
return res.status(500).json({
434
type: 'database_error',
435
message: 'Internal database error'
436
});
437
}
438
439
// Generic Sequelize errors
440
return res.status(500).json({
441
type: 'sequelize_error',
442
message: error.message
443
});
444
}
445
446
// Retry pattern for connection errors
447
async function withRetry<T>(
448
operation: () => Promise<T>,
449
maxRetries: number = 3,
450
delay: number = 1000
451
): Promise<T> {
452
let lastError: Error;
453
454
for (let attempt = 1; attempt <= maxRetries; attempt++) {
455
try {
456
return await operation();
457
} catch (error) {
458
lastError = error;
459
460
// Only retry on connection errors
461
if (isConnectionError(error) && attempt < maxRetries) {
462
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
463
await new Promise(resolve => setTimeout(resolve, delay));
464
delay *= 2; // Exponential backoff
465
continue;
466
}
467
468
throw error;
469
}
470
}
471
472
throw lastError!;
473
}
474
475
// Usage with retry
476
const user = await withRetry(async () => {
477
return await User.findByPk(1);
478
});
479
480
// Validation helper
481
async function validateAndCreate<T extends Model>(
482
model: typeof Model,
483
data: any
484
): Promise<T> {
485
try {
486
return await model.create(data);
487
} catch (error) {
488
if (error instanceof ValidationError) {
489
// Transform validation errors for client
490
const validationErrors = error.errors.reduce((acc, err) => {
491
acc[err.path] = err.message;
492
return acc;
493
}, {} as Record<string, string>);
494
495
throw new Error(JSON.stringify({
496
type: 'validation_failed',
497
errors: validationErrors
498
}));
499
}
500
throw error;
501
}
502
}
503
```
504
505
### Custom Error Classes
506
507
Creating application-specific error classes that extend Sequelize errors.
508
509
```typescript { .api }
510
// Custom business logic errors
511
class InsufficientFundsError extends BaseError {
512
constructor(balance: number, required: number) {
513
super(`Insufficient funds: balance ${balance}, required ${required}`);
514
this.name = 'InsufficientFundsError';
515
}
516
}
517
518
class UserNotActiveError extends BaseError {
519
constructor(userId: number) {
520
super(`User ${userId} is not active`);
521
this.name = 'UserNotActiveError';
522
}
523
}
524
525
// Usage in business logic
526
async function transferFunds(fromUserId: number, toUserId: number, amount: number) {
527
return await sequelize.transaction(async (t) => {
528
const fromUser = await User.findByPk(fromUserId, { transaction: t });
529
const toUser = await User.findByPk(toUserId, { transaction: t });
530
531
if (!fromUser?.isActive) {
532
throw new UserNotActiveError(fromUserId);
533
}
534
535
if (fromUser.balance < amount) {
536
throw new InsufficientFundsError(fromUser.balance, amount);
537
}
538
539
// Perform transfer...
540
});
541
}
542
```