or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-utilities.mdconnection-management.mddatabase-operations.mderror-handling.mdindex.mdquery-files.mdquery-formatting.mdtasks-transactions.md

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive error types for different failure scenarios with detailed error information. pg-promise provides specialized error classes for query results, file operations, and statement preparation, along with error codes and comprehensive error context.

3

4

## Capabilities

5

6

### Query Result Errors

7

8

Errors related to unexpected query result counts or data validation.

9

10

```javascript { .api }

11

/**

12

* Query Result Error - thrown when query results don't match expectations

13

*/

14

class QueryResultError extends Error {

15

// Standard error properties

16

name: string // Error name: 'QueryResultError'

17

message: string // Error message

18

stack: string // Stack trace

19

20

// Extended properties

21

result: IResult // Full query result object

22

received: number // Number of rows received

23

code: queryResultErrorCode // Specific error code

24

query: string // SQL query that caused error

25

values: any // Parameter values used

26

27

toString(): string // String representation

28

}

29

30

/**

31

* Query Result Error Codes

32

*/

33

enum queryResultErrorCode {

34

noData = 0, // No data returned when some was expected

35

notEmpty = 1, // Data returned when none was expected

36

multiple = 2 // Multiple rows returned when one was expected

37

}

38

```

39

40

**Usage Examples:**

41

42

```javascript

43

try {

44

// This will throw QueryResultError if no user found

45

const user = await db.one('SELECT * FROM users WHERE id = $1', [999]);

46

} catch (error) {

47

if (error instanceof pgp.errors.QueryResultError) {

48

console.log('Query result error:', {

49

code: error.code,

50

received: error.received,

51

query: error.query,

52

values: error.values

53

});

54

55

switch (error.code) {

56

case pgp.errors.queryResultErrorCode.noData:

57

console.log('No user found with that ID');

58

break;

59

case pgp.errors.queryResultErrorCode.multiple:

60

console.log('Multiple users found, expected one');

61

break;

62

case pgp.errors.queryResultErrorCode.notEmpty:

63

console.log('Expected no results, but got data');

64

break;

65

}

66

}

67

}

68

69

// Handling different result expectations

70

try {

71

await db.none('INSERT INTO users(name, email) VALUES($1, $2) RETURNING id',

72

['John', 'john@example.com']);

73

} catch (error) {

74

if (error.code === pgp.errors.queryResultErrorCode.notEmpty) {

75

console.log('INSERT returned data when none expected');

76

// This happens when using RETURNING with .none()

77

}

78

}

79

80

// Detecting multiple results

81

try {

82

const user = await db.one('SELECT * FROM users WHERE name = $1', ['John']);

83

} catch (error) {

84

if (error.code === pgp.errors.queryResultErrorCode.multiple) {

85

console.log(`Found ${error.received} users named John, expected exactly 1`);

86

// Switch to .many() or add more specific WHERE conditions

87

}

88

}

89

```

90

91

### Query File Errors

92

93

Errors related to SQL file loading, parsing, and processing.

94

95

```javascript { .api }

96

/**

97

* Query File Error - thrown when SQL files cannot be loaded or parsed

98

*/

99

class QueryFileError extends Error {

100

// Standard error properties

101

name: string // Error name: 'QueryFileError'

102

message: string // Error message

103

stack: string // Stack trace

104

105

// Extended properties

106

file: string // File path that caused the error

107

options: IQueryFileOptions // File processing options used

108

error: SQLParsingError // Underlying SQL parsing error (if applicable)

109

110

toString(level?: number): string // String representation with indentation

111

}

112

113

/**

114

* SQL Parsing Error from pg-minify

115

*/

116

interface SQLParsingError {

117

name: string // Error name

118

message: string // Error message

119

position: number // Character position in SQL

120

line: number // Line number

121

column: number // Column number

122

}

123

```

124

125

**Usage Examples:**

126

127

```javascript

128

// File loading errors

129

const queryFile = new pgp.QueryFile('/path/to/nonexistent.sql');

130

if (queryFile.error) {

131

console.error('File error:', {

132

type: queryFile.error.name,

133

message: queryFile.error.message,

134

file: queryFile.error.file

135

});

136

}

137

138

// SQL parsing errors (with minification)

139

const complexQuery = new pgp.QueryFile('sql/invalid-syntax.sql', { minify: true });

140

if (complexQuery.error && complexQuery.error.error) {

141

const parseError = complexQuery.error.error;

142

console.error('SQL parsing error:', {

143

message: parseError.message,

144

line: parseError.line,

145

column: parseError.column,

146

position: parseError.position

147

});

148

}

149

150

// Error handling in queries

151

try {

152

const results = await db.any(new pgp.QueryFile('sql/problematic-query.sql'));

153

} catch (error) {

154

if (error instanceof pgp.errors.QueryFileError) {

155

console.error('Query file error:', {

156

file: error.file,

157

options: error.options,

158

message: error.message

159

});

160

161

if (error.error) {

162

console.error('Parsing details:', {

163

line: error.error.line,

164

column: error.error.column,

165

position: error.error.position

166

});

167

}

168

}

169

}

170

171

// Development-time error checking

172

function validateQueryFiles(directory) {

173

const files = pgp.utils.enumSql(directory, { recursive: true });

174

const errors = [];

175

176

for (const [name, path] of Object.entries(files)) {

177

const queryFile = new pgp.QueryFile(path, { minify: true });

178

if (queryFile.error) {

179

errors.push({

180

name,

181

path,

182

error: queryFile.error.message

183

});

184

}

185

}

186

187

return errors;

188

}

189

```

190

191

### Prepared Statement Errors

192

193

Errors related to prepared statement configuration and validation.

194

195

```javascript { .api }

196

/**

197

* Prepared Statement Error - thrown when prepared statements are invalid

198

*/

199

class PreparedStatementError extends Error {

200

// Standard error properties

201

name: string // Error name: 'PreparedStatementError'

202

message: string // Error message

203

stack: string // Stack trace

204

205

// Extended properties

206

error: QueryFileError // Underlying QueryFile error (if statement uses QueryFile)

207

208

toString(level?: number): string // String representation with indentation

209

}

210

```

211

212

**Usage Examples:**

213

214

```javascript

215

// Invalid prepared statement

216

const invalidStmt = new pgp.PreparedStatement({

217

name: '', // Empty name will cause error

218

text: 'SELECT * FROM users'

219

});

220

221

const result = invalidStmt.parse();

222

if (result instanceof pgp.errors.PreparedStatementError) {

223

console.error('Prepared statement error:', result.message);

224

}

225

226

// Prepared statement with invalid QueryFile

227

const fileStmt = new pgp.PreparedStatement({

228

name: 'get-users',

229

text: new pgp.QueryFile('sql/invalid.sql')

230

});

231

232

try {

233

await db.any(fileStmt);

234

} catch (error) {

235

if (error instanceof pgp.errors.PreparedStatementError) {

236

console.error('Prepared statement error:', error.message);

237

238

if (error.error) {

239

console.error('Underlying file error:', error.error.message);

240

}

241

}

242

}

243

244

// Validation before execution

245

function validatePreparedStatement(stmt) {

246

const parsed = stmt.parse();

247

248

if (parsed instanceof pgp.errors.PreparedStatementError) {

249

return {

250

valid: false,

251

error: parsed.message,

252

details: parsed.error?.message

253

};

254

}

255

256

return {

257

valid: true,

258

statement: parsed

259

};

260

}

261

262

const stmt = new pgp.PreparedStatement({

263

name: 'test-stmt',

264

text: 'SELECT * FROM users WHERE id = $1'

265

});

266

267

const validation = validatePreparedStatement(stmt);

268

if (validation.valid) {

269

console.log('Statement is valid:', validation.statement.name);

270

} else {

271

console.error('Statement is invalid:', validation.error);

272

}

273

```

274

275

### Parameterized Query Errors

276

277

Errors related to parameterized query configuration and validation.

278

279

```javascript { .api }

280

/**

281

* Parameterized Query Error - thrown when parameterized queries are invalid

282

*/

283

class ParameterizedQueryError extends Error {

284

// Standard error properties

285

name: string // Error name: 'ParameterizedQueryError'

286

message: string // Error message

287

stack: string // Stack trace

288

289

// Extended properties

290

error: QueryFileError // Underlying QueryFile error (if query uses QueryFile)

291

292

toString(level?: number): string // String representation with indentation

293

}

294

```

295

296

**Usage Examples:**

297

298

```javascript

299

// Invalid parameterized query

300

const invalidQuery = new pgp.ParameterizedQuery({

301

text: '', // Empty text will cause error

302

values: [1, 2, 3]

303

});

304

305

const result = invalidQuery.parse();

306

if (result instanceof pgp.errors.ParameterizedQueryError) {

307

console.error('Parameterized query error:', result.message);

308

}

309

310

// Query with file error

311

const fileQuery = new pgp.ParameterizedQuery({

312

text: new pgp.QueryFile('sql/broken.sql'),

313

values: [123]

314

});

315

316

try {

317

await db.any(fileQuery);

318

} catch (error) {

319

if (error instanceof pgp.errors.ParameterizedQueryError) {

320

console.error('Parameterized query error:', error.message);

321

322

if (error.error && error.error.error) {

323

const parseError = error.error.error;

324

console.error('SQL parsing error at line', parseError.line, ':', parseError.message);

325

}

326

}

327

}

328

329

// Parameter count validation

330

function validateParameterCount(query, expectedParams) {

331

const paramCount = (query.text.match(/\$/g) || []).length;

332

const providedCount = query.values ? query.values.length : 0;

333

334

if (paramCount !== providedCount) {

335

console.warn(`Parameter mismatch: query has ${paramCount} parameters, but ${providedCount} values provided`);

336

return false;

337

}

338

339

if (paramCount !== expectedParams) {

340

console.warn(`Expected ${expectedParams} parameters, but query has ${paramCount}`);

341

return false;

342

}

343

344

return true;

345

}

346

```

347

348

### Database Connection Errors

349

350

Standard PostgreSQL and node-postgres errors enhanced with context.

351

352

**Usage Examples:**

353

354

```javascript

355

// Connection error handling

356

try {

357

const db = pgp('postgres://invalid:invalid@nonexistent:5432/db');

358

await db.any('SELECT 1');

359

} catch (error) {

360

console.error('Connection error:', {

361

code: error.code, // ECONNREFUSED, ENOTFOUND, etc.

362

message: error.message,

363

host: error.host,

364

port: error.port

365

});

366

}

367

368

// Query timeout handling

369

try {

370

await db.any('SELECT pg_sleep(10)'); // Long-running query

371

} catch (error) {

372

if (error.code === '57014') { // Query timeout

373

console.error('Query timed out');

374

}

375

}

376

377

// Constraint violation handling

378

try {

379

await db.none('INSERT INTO users(email) VALUES($1)', ['duplicate@example.com']);

380

} catch (error) {

381

if (error.code === '23505') { // Unique violation

382

console.error('Email already exists:', error.detail);

383

} else if (error.code === '23503') { // Foreign key violation

384

console.error('Foreign key constraint violated:', error.detail);

385

} else if (error.code === '23514') { // Check constraint violation

386

console.error('Check constraint violated:', error.detail);

387

}

388

}

389

390

// Transaction rollback handling

391

try {

392

await db.tx(async t => {

393

await t.none('INSERT INTO users(name) VALUES($1)', ['John']);

394

await t.none('INSERT INTO invalid_table(data) VALUES($1)', ['test']); // Will fail

395

});

396

} catch (error) {

397

console.error('Transaction failed:', error.message);

398

// Transaction automatically rolled back

399

}

400

```

401

402

### Error Context and Debugging

403

404

Comprehensive error context for debugging and monitoring.

405

406

**Usage Examples:**

407

408

```javascript

409

// Global error handling with context

410

const pgp = require('pg-promise')({

411

error: (err, e) => {

412

console.error('Database error occurred:', {

413

error: {

414

name: err.name,

415

message: err.message,

416

code: err.code,

417

severity: err.severity,

418

detail: err.detail,

419

hint: err.hint,

420

position: err.position,

421

where: err.where,

422

schema: err.schema,

423

table: err.table,

424

column: err.column,

425

dataType: err.dataType,

426

constraint: err.constraint

427

},

428

context: {

429

query: e.query,

430

params: e.params,

431

values: e.values,

432

client: e.client.processID,

433

database: e.client.database,

434

user: e.client.user,

435

task: e.ctx?.tag,

436

level: e.ctx?.level,

437

duration: e.ctx ? Date.now() - e.ctx.start.getTime() : null

438

}

439

});

440

}

441

});

442

443

// Error logging wrapper

444

async function executeWithLogging(operation, context = {}) {

445

const start = Date.now();

446

447

try {

448

const result = await operation();

449

console.log('Operation succeeded:', {

450

...context,

451

duration: Date.now() - start,

452

success: true

453

});

454

return result;

455

} catch (error) {

456

console.error('Operation failed:', {

457

...context,

458

duration: Date.now() - start,

459

success: false,

460

error: {

461

type: error.constructor.name,

462

message: error.message,

463

code: error.code

464

}

465

});

466

throw error;

467

}

468

}

469

470

// Usage

471

const users = await executeWithLogging(

472

() => db.any('SELECT * FROM users WHERE active = $1', [true]),

473

{ operation: 'get-active-users', module: 'user-service' }

474

);

475

```

476

477

### Error Recovery Patterns

478

479

Common patterns for error handling and recovery.

480

481

**Usage Examples:**

482

483

```javascript

484

// Retry with exponential backoff

485

async function retryQuery(queryFunc, maxRetries = 3, baseDelay = 1000) {

486

for (let attempt = 1; attempt <= maxRetries; attempt++) {

487

try {

488

return await queryFunc();

489

} catch (error) {

490

const isRetryable = error.code === 'ECONNRESET' ||

491

error.code === 'ECONNREFUSED' ||

492

error.code === '40001'; // Serialization failure

493

494

if (!isRetryable || attempt === maxRetries) {

495

throw error;

496

}

497

498

const delay = baseDelay * Math.pow(2, attempt - 1);

499

console.log(`Query failed (attempt ${attempt}/${maxRetries}), retrying in ${delay}ms...`);

500

await new Promise(resolve => setTimeout(resolve, delay));

501

}

502

}

503

}

504

505

// Graceful degradation

506

async function getUserWithFallback(userId) {

507

try {

508

// Try to get user with profile

509

return await db.one(`

510

SELECT u.*, p.bio, p.avatar_url

511

FROM users u

512

LEFT JOIN profiles p ON u.id = p.user_id

513

WHERE u.id = $1

514

`, [userId]);

515

} catch (error) {

516

if (error instanceof pgp.errors.QueryResultError &&

517

error.code === pgp.errors.queryResultErrorCode.noData) {

518

return null; // User not found

519

}

520

521

// On other errors, try simpler query

522

try {

523

console.warn('Profile query failed, falling back to basic user data');

524

return await db.one('SELECT * FROM users WHERE id = $1', [userId]);

525

} catch (fallbackError) {

526

if (fallbackError instanceof pgp.errors.QueryResultError &&

527

fallbackError.code === pgp.errors.queryResultErrorCode.noData) {

528

return null;

529

}

530

throw fallbackError; // Re-throw if not a "not found" error

531

}

532

}

533

}

534

535

// Circuit breaker pattern

536

class DatabaseCircuitBreaker {

537

constructor(threshold = 5, timeout = 60000) {

538

this.failureThreshold = threshold;

539

this.timeout = timeout;

540

this.failureCount = 0;

541

this.lastFailureTime = null;

542

this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN

543

}

544

545

async execute(operation) {

546

if (this.state === 'OPEN') {

547

if (Date.now() - this.lastFailureTime > this.timeout) {

548

this.state = 'HALF_OPEN';

549

} else {

550

throw new Error('Circuit breaker is OPEN');

551

}

552

}

553

554

try {

555

const result = await operation();

556

this.onSuccess();

557

return result;

558

} catch (error) {

559

this.onFailure();

560

throw error;

561

}

562

}

563

564

onSuccess() {

565

this.failureCount = 0;

566

this.state = 'CLOSED';

567

}

568

569

onFailure() {

570

this.failureCount++;

571

this.lastFailureTime = Date.now();

572

573

if (this.failureCount >= this.failureThreshold) {

574

this.state = 'OPEN';

575

}

576

}

577

}

578

579

const circuitBreaker = new DatabaseCircuitBreaker();

580

581

// Usage

582

const users = await circuitBreaker.execute(() =>

583

db.any('SELECT * FROM users WHERE active = true')

584

);

585

```

586

587

## Types

588

589

```javascript { .api }

590

// Error interfaces

591

interface IErrorContext {

592

client: IClient // Database client

593

cn: any // Connection configuration

594

dc: any // Database context

595

query: any // Query being executed

596

params: any // Query parameters

597

values: any // Parameter values

598

queryFilePath?: string // Query file path (if applicable)

599

ctx: ITaskContext // Task context

600

}

601

602

// Standard PostgreSQL error properties

603

interface IPostgreSQLError {

604

name: string // Error name

605

message: string // Error message

606

severity: string // Error severity (ERROR, FATAL, etc.)

607

code: string // PostgreSQL error code

608

detail?: string // Error detail

609

hint?: string // Error hint

610

position?: string // Error position in query

611

internalPosition?: string // Internal error position

612

internalQuery?: string // Internal query

613

where?: string // Error context

614

schema?: string // Schema name

615

table?: string // Table name

616

column?: string // Column name

617

dataType?: string // Data type name

618

constraint?: string // Constraint name

619

file?: string // Source file name

620

line?: string // Source line number

621

routine?: string // Function name

622

}

623

624

// Result interface

625

interface IResult {

626

rows: any[] // Result rows

627

rowCount: number // Number of rows

628

command: string // SQL command

629

oid: number // Object ID

630

fields: IFieldInfo[] // Field information

631

}

632

633

interface IFieldInfo {

634

name: string // Field name

635

tableID: number // Table OID

636

columnID: number // Column number

637

dataTypeID: number // Data type OID

638

dataTypeSize: number // Data type size

639

dataTypeModifier: number // Type modifier

640

format: string // Format code

641

}

642

```