0
# Error Handling
1
2
Comprehensive error handling with retryable error detection and detailed error information for robust application development.
3
4
## Capabilities
5
6
### Neo4j Error Class
7
8
Base error class for all Neo4j-specific errors with detailed error codes and messages.
9
10
```typescript { .api }
11
class Neo4jError extends Error {
12
/** Neo4j error code (e.g., "Neo.ClientError.Statement.SyntaxError") */
13
code: string;
14
15
/**
16
* Create a new Neo4j error
17
* @param message - Error message describing the issue
18
* @param code - Optional Neo4j error code
19
*/
20
constructor(message: string, code?: string);
21
}
22
23
/**
24
* Create a new Neo4j error
25
* @param message - Error message
26
* @param code - Optional error code
27
* @returns Neo4jError instance
28
*/
29
function newError(message: string, code?: string): Neo4jError;
30
```
31
32
**Usage Examples:**
33
34
```typescript
35
import { driver, auth, Neo4jError, newError } from "neo4j-driver";
36
37
const session = neo4jDriver.session();
38
39
try {
40
// This will cause a syntax error
41
await session.run("INVALID CYPHER QUERY");
42
} catch (error) {
43
if (error instanceof Neo4jError) {
44
console.log(`Neo4j Error: ${error.message}`);
45
console.log(`Error Code: ${error.code}`);
46
47
// Handle specific error types
48
switch (error.code) {
49
case "Neo.ClientError.Statement.SyntaxError":
50
console.log("Cypher syntax error - check your query");
51
break;
52
case "Neo.ClientError.Security.Unauthorized":
53
console.log("Authentication failed - check credentials");
54
break;
55
case "Neo.TransientError.Network.DatabaseUnavailable":
56
console.log("Database unavailable - retry later");
57
break;
58
default:
59
console.log(`Unhandled error type: ${error.code}`);
60
}
61
} else {
62
console.log(`Other error: ${error.message}`);
63
}
64
} finally {
65
await session.close();
66
}
67
68
// Creating custom Neo4j errors
69
function validateInput(data: any) {
70
if (!data.name) {
71
throw newError("Name is required", "App.Validation.MissingName");
72
}
73
if (data.age < 0) {
74
throw newError("Age cannot be negative", "App.Validation.InvalidAge");
75
}
76
}
77
```
78
79
### Retryable Error Detection
80
81
Utility function to determine if an error should be retried.
82
83
```typescript { .api }
84
/**
85
* Check if an error is retryable (transient error that may succeed on retry)
86
* @param error - Error to check for retryability
87
* @returns true if the error is retryable, false otherwise
88
*/
89
function isRetryableError(error: any): boolean;
90
```
91
92
**Usage Examples:**
93
94
```typescript
95
import { isRetryableError } from "neo4j-driver";
96
97
async function executeWithRetry<T>(operation: () => Promise<T>, maxRetries: number = 3): Promise<T> {
98
let lastError: any;
99
100
for (let attempt = 1; attempt <= maxRetries; attempt++) {
101
try {
102
return await operation();
103
} catch (error) {
104
lastError = error;
105
106
if (!isRetryableError(error)) {
107
console.log(`Non-retryable error: ${error.message}`);
108
throw error;
109
}
110
111
if (attempt === maxRetries) {
112
console.log(`Max retries (${maxRetries}) reached`);
113
throw error;
114
}
115
116
const delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
117
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
118
await new Promise(resolve => setTimeout(resolve, delay));
119
}
120
}
121
122
throw lastError;
123
}
124
125
// Usage with session operations
126
const session = neo4jDriver.session();
127
128
try {
129
const result = await executeWithRetry(async () => {
130
return await session.run(`
131
MATCH (p:Person {name: $name})
132
SET p.lastAccessed = datetime()
133
RETURN p
134
`, { name: "Alice" });
135
});
136
137
console.log("Operation succeeded after retries");
138
} catch (error) {
139
console.error("Operation failed after all retries:", error.message);
140
} finally {
141
await session.close();
142
}
143
144
// Check specific errors
145
try {
146
await session.run("MATCH (n) RETURN n");
147
} catch (error) {
148
if (isRetryableError(error)) {
149
console.log("This error can be retried:", error.message);
150
// Implement retry logic
151
} else {
152
console.log("This error should not be retried:", error.message);
153
// Handle as permanent failure
154
}
155
}
156
```
157
158
### Error Code Constants
159
160
Predefined constants for common Neo4j error codes.
161
162
```typescript { .api }
163
declare const error: {
164
/** Service unavailable error code */
165
SERVICE_UNAVAILABLE: "Neo.TransientError.General.DatabaseUnavailable";
166
167
/** Session expired error code */
168
SESSION_EXPIRED: "Neo.TransientError.Transaction.Terminated";
169
170
/** Protocol error code */
171
PROTOCOL_ERROR: "Neo.ClientError.Request.Invalid";
172
};
173
```
174
175
**Usage Examples:**
176
177
```typescript
178
import { error } from "neo4j-driver";
179
180
try {
181
const result = await session.run("MATCH (n) RETURN count(n)");
182
} catch (err) {
183
if (err instanceof Neo4jError) {
184
switch (err.code) {
185
case error.SERVICE_UNAVAILABLE:
186
console.log("Database is unavailable - implement retry logic");
187
// Retry with exponential backoff
188
break;
189
190
case error.SESSION_EXPIRED:
191
console.log("Session expired - create new session");
192
// Close current session and create new one
193
break;
194
195
case error.PROTOCOL_ERROR:
196
console.log("Protocol error - check client/server compatibility");
197
// Log for debugging, don't retry
198
break;
199
200
case "Neo.ClientError.Statement.SyntaxError":
201
console.log("Cypher syntax error - fix the query");
202
break;
203
204
case "Neo.ClientError.Schema.ConstraintValidationFailed":
205
console.log("Constraint violation - handle business logic error");
206
break;
207
208
default:
209
console.log(`Unhandled error: ${err.code} - ${err.message}`);
210
}
211
}
212
}
213
214
// Using error constants in conditions
215
function shouldRetryError(error: any): boolean {
216
if (!error.code) return false;
217
218
const retryableCodes = [
219
error.SERVICE_UNAVAILABLE,
220
error.SESSION_EXPIRED,
221
"Neo.TransientError.Network.ConnectionTimeout",
222
"Neo.TransientError.Transaction.DeadlockDetected"
223
];
224
225
return retryableCodes.includes(error.code);
226
}
227
```
228
229
### Error Categories and Handling Patterns
230
231
Different types of errors require different handling strategies.
232
233
**Transient Errors (Retryable):**
234
235
```typescript
236
// Network and availability errors
237
const transientErrors = [
238
"Neo.TransientError.General.DatabaseUnavailable",
239
"Neo.TransientError.Network.ConnectionTimeout",
240
"Neo.TransientError.Network.DatabaseUnavailable",
241
"Neo.TransientError.Transaction.DeadlockDetected",
242
"Neo.TransientError.Transaction.LockClientStopped"
243
];
244
245
async function handleTransientError<T>(
246
operation: () => Promise<T>,
247
maxRetries: number = 3,
248
baseDelay: number = 1000
249
): Promise<T> {
250
for (let attempt = 1; attempt <= maxRetries; attempt++) {
251
try {
252
return await operation();
253
} catch (error) {
254
if (!isRetryableError(error) || attempt === maxRetries) {
255
throw error;
256
}
257
258
const delay = baseDelay * Math.pow(2, attempt - 1);
259
console.log(`Transient error, retrying in ${delay}ms...`);
260
await new Promise(resolve => setTimeout(resolve, delay));
261
}
262
}
263
264
throw new Error("Should not reach here");
265
}
266
```
267
268
**Client Errors (Non-Retryable):**
269
270
```typescript
271
// Syntax, constraint, and security errors
272
const clientErrors = [
273
"Neo.ClientError.Statement.SyntaxError",
274
"Neo.ClientError.Statement.SemanticError",
275
"Neo.ClientError.Schema.ConstraintValidationFailed",
276
"Neo.ClientError.Security.Unauthorized",
277
"Neo.ClientError.Security.Forbidden"
278
];
279
280
function handleClientError(error: Neo4jError): void {
281
switch (error.code) {
282
case "Neo.ClientError.Statement.SyntaxError":
283
console.error("Cypher syntax error:", error.message);
284
// Log query for debugging, don't retry
285
break;
286
287
case "Neo.ClientError.Schema.ConstraintValidationFailed":
288
console.error("Constraint violation:", error.message);
289
// Handle as business logic error
290
throw new Error(`Data validation failed: ${error.message}`);
291
292
case "Neo.ClientError.Security.Unauthorized":
293
console.error("Authentication failed:", error.message);
294
// Redirect to login or refresh token
295
throw new Error("Please log in again");
296
297
case "Neo.ClientError.Security.Forbidden":
298
console.error("Access denied:", error.message);
299
// Handle as authorization error
300
throw new Error("Insufficient permissions");
301
302
default:
303
console.error("Client error:", error.code, error.message);
304
throw error;
305
}
306
}
307
```
308
309
**Database Errors:**
310
311
```typescript
312
// Database-level errors
313
const databaseErrors = [
314
"Neo.DatabaseError.General.UnknownError",
315
"Neo.DatabaseError.Schema.IndexCreationFailed",
316
"Neo.DatabaseError.Transaction.TransactionStartFailed"
317
];
318
319
function handleDatabaseError(error: Neo4jError): void {
320
console.error("Database error:", error.code, error.message);
321
322
// Log for monitoring/alerting
323
logErrorForMonitoring(error);
324
325
// Database errors are usually not retryable by the client
326
throw new Error(`Database error: ${error.message}`);
327
}
328
```
329
330
### Comprehensive Error Handling Example
331
332
```typescript
333
class Neo4jService {
334
private driver: Driver;
335
336
constructor(uri: string, auth: AuthToken) {
337
this.driver = driver(uri, auth);
338
}
339
340
async executeQuery<T>(
341
query: string,
342
parameters: Parameters = {},
343
transformer: (result: Result) => T
344
): Promise<T> {
345
const session = this.driver.session();
346
347
try {
348
const result = await this.withRetry(async () => {
349
return await session.run(query, parameters);
350
});
351
352
return transformer(result);
353
} catch (error) {
354
this.handleError(error, query, parameters);
355
throw error;
356
} finally {
357
await session.close();
358
}
359
}
360
361
private async withRetry<T>(
362
operation: () => Promise<T>,
363
maxRetries: number = 3
364
): Promise<T> {
365
let lastError: any;
366
367
for (let attempt = 1; attempt <= maxRetries; attempt++) {
368
try {
369
return await operation();
370
} catch (error) {
371
lastError = error;
372
373
if (!isRetryableError(error) || attempt === maxRetries) {
374
throw error;
375
}
376
377
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
378
console.log(`Retry attempt ${attempt} after ${delay}ms`);
379
await new Promise(resolve => setTimeout(resolve, delay));
380
}
381
}
382
383
throw lastError;
384
}
385
386
private handleError(error: any, query: string, parameters: Parameters): void {
387
if (!(error instanceof Neo4jError)) {
388
console.error("Non-Neo4j error:", error.message);
389
return;
390
}
391
392
// Log error details
393
console.error("Neo4j Error Details:", {
394
code: error.code,
395
message: error.message,
396
query: query.substring(0, 100) + "...",
397
parameterKeys: Object.keys(parameters)
398
});
399
400
// Categorize and handle error
401
if (error.code.startsWith("Neo.TransientError")) {
402
console.log("Transient error occurred - retry logic applied");
403
} else if (error.code.startsWith("Neo.ClientError")) {
404
this.handleClientError(error);
405
} else if (error.code.startsWith("Neo.DatabaseError")) {
406
this.handleDatabaseError(error);
407
}
408
}
409
410
private handleClientError(error: Neo4jError): void {
411
// Client error handling logic
412
if (error.code.includes("Security")) {
413
// Handle security errors
414
console.error("Security error - check authentication");
415
} else if (error.code.includes("Statement")) {
416
// Handle query errors
417
console.error("Query error - check Cypher syntax");
418
}
419
}
420
421
private handleDatabaseError(error: Neo4jError): void {
422
// Database error handling logic
423
console.error("Database error - may need administrator attention");
424
425
// Could send alert to monitoring system
426
this.sendAlert("Database Error", error);
427
}
428
429
private sendAlert(type: string, error: Neo4jError): void {
430
// Implementation for alerting system
431
console.log(`ALERT: ${type} - ${error.code}: ${error.message}`);
432
}
433
434
async close(): Promise<void> {
435
await this.driver.close();
436
}
437
}
438
439
// Usage
440
const neo4jService = new Neo4jService(
441
"neo4j://localhost:7687",
442
auth.basic("neo4j", "password")
443
);
444
445
try {
446
const people = await neo4jService.executeQuery(
447
"MATCH (p:Person) WHERE p.age > $minAge RETURN p",
448
{ minAge: 25 },
449
(result) => result.records.map(record => record.get("p").properties)
450
);
451
452
console.log(`Found ${people.length} people`);
453
} catch (error) {
454
console.error("Failed to fetch people:", error.message);
455
} finally {
456
await neo4jService.close();
457
}
458
```