or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mddriver-management.mderror-handling.mdgraph-types.mdindex.mdreactive-programming.mdsession-operations.mdtemporal-types.mdtransaction-management.md

error-handling.mddocs/

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

```