or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

connections.mderrors.mdindex.mdlarge-objects.mdnotifications.mdquery-processing.mdquerying.mdreplication.mdtransactions.mdtypes.md

errors.mddocs/

0

# Error Handling

1

2

Comprehensive error handling and PostgreSQL error types with detailed diagnostic information and recovery strategies.

3

4

## Capabilities

5

6

### PostgresError Class

7

8

Specialized error class providing detailed PostgreSQL server error information.

9

10

```javascript { .api }

11

/**

12

* PostgreSQL-specific error class

13

* Extends standard Error with PostgreSQL server error details

14

*/

15

class PostgresError extends Error {

16

/** Error severity level */

17

severity: string;

18

19

/** Localized severity level */

20

severity_local: string;

21

22

/** PostgreSQL error code (SQLSTATE) */

23

code: string;

24

25

/** Error message */

26

message: string;

27

28

/** Additional error details */

29

detail?: string;

30

31

/** Hint for resolving the error */

32

hint?: string;

33

34

/** Character position in query where error occurred */

35

position?: string;

36

37

/** Internal query position */

38

internal_position?: string;

39

40

/** Internal query that caused error */

41

internal_query?: string;

42

43

/** Where in source code error occurred */

44

where?: string;

45

46

/** Schema name related to error */

47

schema_name?: string;

48

49

/** Table name related to error */

50

table_name?: string;

51

52

/** Column name related to error */

53

column_name?: string;

54

55

/** Data type name related to error */

56

datatype_name?: string;

57

58

/** Constraint name that was violated */

59

constraint_name?: string;

60

61

/** Source file where error occurred */

62

file?: string;

63

64

/** Line number in source file */

65

line?: string;

66

67

/** Routine name where error occurred */

68

routine?: string;

69

70

/** Query that caused the error (when debug enabled) */

71

query?: string;

72

73

/** Parameters used in query (when debug enabled) */

74

parameters?: any[];

75

}

76

```

77

78

**Usage Examples:**

79

80

```javascript

81

try {

82

await sql`INSERT INTO users (email) VALUES (${email})`;

83

} catch (error) {

84

if (error instanceof PostgresError) {

85

console.log('PostgreSQL Error Details:');

86

console.log('Code:', error.code);

87

console.log('Message:', error.message);

88

console.log('Detail:', error.detail);

89

console.log('Hint:', error.hint);

90

91

if (error.constraint_name) {

92

console.log('Constraint violated:', error.constraint_name);

93

}

94

95

if (error.position) {

96

console.log('Error at position:', error.position);

97

}

98

} else {

99

console.log('Unexpected error:', error);

100

}

101

}

102

```

103

104

### Connection Error Types

105

106

Additional error types for connection-related failures and client-side errors.

107

108

```javascript { .api }

109

/**

110

* Connection-related error class

111

* For network and connection pool errors

112

*/

113

interface ConnectionError extends Error {

114

code:

115

| 'CONNECTION_DESTROYED'

116

| 'CONNECT_TIMEOUT'

117

| 'CONNECTION_CLOSED'

118

| 'CONNECTION_ENDED';

119

errno: string;

120

address: string;

121

port?: number;

122

}

123

124

/**

125

* Protocol or feature not supported error

126

*/

127

interface NotSupportedError extends Error {

128

code: 'MESSAGE_NOT_SUPPORTED';

129

name: string;

130

}

131

132

/**

133

* Generic client-side errors

134

*/

135

interface GenericError extends Error {

136

code:

137

| '57014' // canceling statement due to user request

138

| 'NOT_TAGGED_CALL'

139

| 'UNDEFINED_VALUE'

140

| 'MAX_PARAMETERS_EXCEEDED'

141

| 'SASL_SIGNATURE_MISMATCH'

142

| 'UNSAFE_TRANSACTION';

143

message: string;

144

}

145

146

/**

147

* Authentication method not implemented error

148

*/

149

interface AuthNotImplementedError extends Error {

150

code: 'AUTH_TYPE_NOT_IMPLEMENTED';

151

type: number | string;

152

message: string;

153

}

154

```

155

156

**Usage Examples:**

157

158

```javascript

159

try {

160

await sql`SELECT * FROM users`;

161

} catch (error) {

162

if (error.code === 'CONNECTION_DESTROYED') {

163

console.log('Connection was destroyed, reconnecting...');

164

// Handle reconnection logic

165

} else if (error.code === 'CONNECT_TIMEOUT') {

166

console.log('Connection timeout, retrying...');

167

// Handle timeout retry logic

168

} else if (error.code === 'NOT_TAGGED_CALL') {

169

console.log('Must use tagged template literals');

170

// Fix query syntax

171

} else if (error.code === 'AUTH_TYPE_NOT_IMPLEMENTED') {

172

console.log('Authentication method not supported:', error.type);

173

// Handle auth configuration

174

}

175

}

176

```

177

178

### Common Error Codes

179

180

Handle specific PostgreSQL error conditions with appropriate responses.

181

182

```javascript { .api }

183

/**

184

* Common PostgreSQL error codes (SQLSTATE)

185

*/

186

const ERROR_CODES = {

187

// Connection errors

188

CONNECTION_EXCEPTION: '08000',

189

CONNECTION_DOES_NOT_EXIST: '08003',

190

CONNECTION_FAILURE: '08006',

191

192

// Data errors

193

DATA_EXCEPTION: '22000',

194

NUMERIC_VALUE_OUT_OF_RANGE: '22003',

195

INVALID_DATETIME_FORMAT: '22007',

196

DIVISION_BY_ZERO: '22012',

197

198

// Integrity constraint violations

199

INTEGRITY_CONSTRAINT_VIOLATION: '23000',

200

RESTRICT_VIOLATION: '23001',

201

NOT_NULL_VIOLATION: '23502',

202

FOREIGN_KEY_VIOLATION: '23503',

203

UNIQUE_VIOLATION: '23505',

204

CHECK_VIOLATION: '23514',

205

206

// Transaction errors

207

TRANSACTION_ROLLBACK: '25000',

208

SERIALIZATION_FAILURE: '25001',

209

DEADLOCK_DETECTED: '25P01',

210

211

// System errors

212

SYSTEM_ERROR: '58000',

213

DISK_FULL: '58030',

214

INSUFFICIENT_RESOURCES: '53000',

215

OUT_OF_MEMORY: '53200',

216

TOO_MANY_CONNECTIONS: '53300',

217

218

// Security errors

219

INSUFFICIENT_PRIVILEGE: '42501',

220

INVALID_AUTHORIZATION: '28000',

221

INVALID_PASSWORD: '28P01'

222

};

223

```

224

225

**Usage Examples:**

226

227

```javascript

228

async function handleDatabaseOperation() {

229

try {

230

await sql`INSERT INTO orders (user_id, product_id) VALUES (${userId}, ${productId})`;

231

} catch (error) {

232

if (error.code === '23503') {

233

// Foreign key violation

234

throw new Error('Invalid user or product ID');

235

} else if (error.code === '23505') {

236

// Unique violation

237

throw new Error('Duplicate order detected');

238

} else if (error.code === '23502') {

239

// Not null violation

240

throw new Error('Required field is missing');

241

} else {

242

// Unexpected error

243

throw error;

244

}

245

}

246

}

247

248

// More comprehensive error handling

249

async function createUser(userData) {

250

try {

251

const [user] = await sql`

252

INSERT INTO users (email, username, password_hash)

253

VALUES (${userData.email}, ${userData.username}, ${userData.passwordHash})

254

RETURNING *

255

`;

256

return user;

257

} catch (error) {

258

switch (error.code) {

259

case '23505':

260

if (error.constraint_name === 'users_email_unique') {

261

throw new Error('Email address already registered');

262

} else if (error.constraint_name === 'users_username_unique') {

263

throw new Error('Username already taken');

264

}

265

break;

266

267

case '23502':

268

throw new Error(`Required field missing: ${error.column_name}`);

269

270

case '22001':

271

throw new Error('Input data too long for field');

272

273

case '53300':

274

throw new Error('Server too busy, please try again later');

275

276

default:

277

console.error('Unexpected database error:', error);

278

throw new Error('User creation failed');

279

}

280

}

281

}

282

```

283

284

## Error Recovery Strategies

285

286

### Automatic Retry Logic

287

288

Implement smart retry logic for transient errors.

289

290

```javascript { .api }

291

/**

292

* Retry configuration options

293

*/

294

interface RetryOptions {

295

/** Maximum number of retry attempts */

296

maxAttempts: number;

297

298

/** Base delay between retries in milliseconds */

299

baseDelay: number;

300

301

/** Whether to use exponential backoff */

302

exponentialBackoff: boolean;

303

304

/** Maximum delay between retries */

305

maxDelay: number;

306

307

/** Error codes that should trigger retry */

308

retryableErrors: string[];

309

}

310

```

311

312

**Usage Examples:**

313

314

```javascript

315

class DatabaseRetryHandler {

316

constructor(options = {}) {

317

this.options = {

318

maxAttempts: 3,

319

baseDelay: 1000,

320

exponentialBackoff: true,

321

maxDelay: 10000,

322

retryableErrors: [

323

'08000', // Connection exception

324

'08003', // Connection does not exist

325

'08006', // Connection failure

326

'25001', // Serialization failure

327

'25P01', // Deadlock detected

328

'53000', // Insufficient resources

329

'53200', // Out of memory

330

'53300', // Too many connections

331

],

332

...options

333

};

334

}

335

336

async executeWithRetry(operation, context = {}) {

337

let lastError;

338

339

for (let attempt = 1; attempt <= this.options.maxAttempts; attempt++) {

340

try {

341

return await operation();

342

} catch (error) {

343

lastError = error;

344

345

if (!this.shouldRetry(error, attempt)) {

346

throw error;

347

}

348

349

const delay = this.calculateDelay(attempt);

350

console.log(`Operation failed (attempt ${attempt}), retrying in ${delay}ms:`, error.message);

351

352

await this.sleep(delay);

353

}

354

}

355

356

throw lastError;

357

}

358

359

shouldRetry(error, attempt) {

360

// Don't retry if max attempts reached

361

if (attempt >= this.options.maxAttempts) {

362

return false;

363

}

364

365

// Only retry for PostgreSQL errors with retryable codes

366

if (!(error instanceof PostgresError)) {

367

return false;

368

}

369

370

return this.options.retryableErrors.includes(error.code);

371

}

372

373

calculateDelay(attempt) {

374

let delay = this.options.baseDelay;

375

376

if (this.options.exponentialBackoff) {

377

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

378

}

379

380

return Math.min(delay, this.options.maxDelay);

381

}

382

383

sleep(ms) {

384

return new Promise(resolve => setTimeout(resolve, ms));

385

}

386

}

387

388

// Usage

389

const retryHandler = new DatabaseRetryHandler({

390

maxAttempts: 5,

391

baseDelay: 500,

392

exponentialBackoff: true

393

});

394

395

const result = await retryHandler.executeWithRetry(async () => {

396

return await sql`SELECT * FROM users WHERE id = ${userId}`;

397

});

398

```

399

400

### Transaction Error Handling

401

402

Handle transaction-specific errors and implement proper rollback strategies.

403

404

```javascript

405

async function performComplexTransaction() {

406

let transaction;

407

408

try {

409

transaction = await sql.begin(async (sql) => {

410

// Step 1: Create order

411

const [order] = await sql`

412

INSERT INTO orders (user_id, total_amount)

413

VALUES (${userId}, ${totalAmount})

414

RETURNING *

415

`;

416

417

// Step 2: Create order items

418

for (const item of orderItems) {

419

await sql`

420

INSERT INTO order_items (order_id, product_id, quantity, price)

421

VALUES (${order.id}, ${item.productId}, ${item.quantity}, ${item.price})

422

`;

423

424

// Step 3: Update inventory

425

const [updated] = await sql`

426

UPDATE products

427

SET stock_quantity = stock_quantity - ${item.quantity}

428

WHERE id = ${item.productId} AND stock_quantity >= ${item.quantity}

429

RETURNING stock_quantity

430

`;

431

432

if (!updated) {

433

throw new Error(`Insufficient stock for product ${item.productId}`);

434

}

435

}

436

437

return order;

438

});

439

440

return transaction;

441

442

} catch (error) {

443

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

444

445

if (error.code === '25001') {

446

// Serialization failure - retry the entire transaction

447

console.log('Serialization conflict detected, retrying transaction...');

448

return performComplexTransaction();

449

} else if (error.code === '25P01') {

450

// Deadlock detected

451

console.log('Deadlock detected, retrying after delay...');

452

await new Promise(resolve => setTimeout(resolve, Math.random() * 1000));

453

return performComplexTransaction();

454

} else {

455

// Non-retryable error

456

throw error;

457

}

458

}

459

}

460

```

461

462

## Debug Configuration

463

464

Enable detailed error information for development and troubleshooting.

465

466

```javascript { .api }

467

/**

468

* Debug configuration options

469

*/

470

const sql = postgres(connectionConfig, {

471

/** Enable debug mode */

472

debug: true,

473

474

/** Custom debug function */

475

debug: (connection, query, parameters, paramTypes) => {

476

console.log('Query:', query);

477

console.log('Parameters:', parameters);

478

console.log('Connection:', connection);

479

}

480

});

481

```

482

483

**Usage Examples:**

484

485

```javascript

486

// Debug mode with detailed logging

487

const debugSql = postgres(connectionConfig, {

488

debug: (connection, query, parameters) => {

489

console.log(`[Connection ${connection}] Executing query:`);

490

console.log('SQL:', query);

491

console.log('Parameters:', parameters);

492

console.log('---');

493

}

494

});

495

496

try {

497

await debugSql`SELECT * FROM users WHERE email = ${email}`;

498

} catch (error) {

499

// Error will include query and parameters when debug is enabled

500

console.log('Failed query:', error.query);

501

console.log('Query parameters:', error.parameters);

502

console.log('Full error:', error);

503

}

504

505

// Conditional debug based on environment

506

const sql = postgres(connectionConfig, {

507

debug: process.env.NODE_ENV === 'development' ? console.log : false

508

});

509

510

// Custom debug with filtering

511

const sql = postgres(connectionConfig, {

512

debug: (connection, query, parameters) => {

513

// Only log slow queries or errors

514

if (query.toLowerCase().includes('select') && parameters.length > 5) {

515

console.log('Complex query detected:', query);

516

}

517

}

518

});

519

```

520

521

## Error Monitoring and Logging

522

523

### Structured Error Logging

524

525

Implement comprehensive error logging for production monitoring.

526

527

```javascript

528

class DatabaseErrorLogger {

529

constructor(logger) {

530

this.logger = logger;

531

}

532

533

logError(error, context = {}) {

534

const errorInfo = {

535

timestamp: new Date().toISOString(),

536

error_type: error.constructor.name,

537

...context

538

};

539

540

if (error instanceof PostgresError) {

541

errorInfo.postgres_error = {

542

code: error.code,

543

severity: error.severity,

544

message: error.message,

545

detail: error.detail,

546

hint: error.hint,

547

position: error.position,

548

constraint_name: error.constraint_name,

549

table_name: error.table_name,

550

column_name: error.column_name,

551

query: error.query,

552

parameters: error.parameters

553

};

554

} else {

555

errorInfo.generic_error = {

556

message: error.message,

557

stack: error.stack

558

};

559

}

560

561

this.logger.error('Database operation failed', errorInfo);

562

}

563

564

async wrapOperation(operation, context) {

565

try {

566

return await operation();

567

} catch (error) {

568

this.logError(error, context);

569

throw error;

570

}

571

}

572

}

573

574

// Usage with structured logging

575

import winston from 'winston';

576

577

const logger = winston.createLogger({

578

level: 'info',

579

format: winston.format.json(),

580

transports: [

581

new winston.transports.File({ filename: 'database-errors.log', level: 'error' }),

582

new winston.transports.Console({ format: winston.format.simple() })

583

]

584

});

585

586

const errorLogger = new DatabaseErrorLogger(logger);

587

588

// Wrap database operations

589

const user = await errorLogger.wrapOperation(

590

() => sql`SELECT * FROM users WHERE id = ${userId}`,

591

{ operation: 'get_user', user_id: userId }

592

);

593

```

594

595

### Error Metrics and Alerting

596

597

Track error patterns and implement alerting for critical issues.

598

599

```javascript

600

class DatabaseErrorMetrics {

601

constructor() {

602

this.errorCounts = new Map();

603

this.errorRates = new Map();

604

this.lastReset = Date.now();

605

}

606

607

recordError(error) {

608

const code = error.code || 'UNKNOWN';

609

610

// Count errors by type

611

this.errorCounts.set(code, (this.errorCounts.get(code) || 0) + 1);

612

613

// Track error rates (errors per minute)

614

const minute = Math.floor(Date.now() / 60000);

615

const rateKey = `${code}:${minute}`;

616

this.errorRates.set(rateKey, (this.errorRates.get(rateKey) || 0) + 1);

617

618

// Check for alert conditions

619

this.checkAlerts(code);

620

}

621

622

checkAlerts(errorCode) {

623

const count = this.errorCounts.get(errorCode) || 0;

624

const minute = Math.floor(Date.now() / 60000);

625

const rateKey = `${errorCode}:${minute}`;

626

const rate = this.errorRates.get(rateKey) || 0;

627

628

// Alert on high error rates

629

if (rate > 10) {

630

this.sendAlert(`High error rate for ${errorCode}: ${rate} errors/minute`);

631

}

632

633

// Alert on specific critical errors

634

if (errorCode === '53300' && count > 0) { // Too many connections

635

this.sendAlert('Database connection pool exhausted');

636

}

637

638

if (errorCode === '58030' && count > 0) { // Disk full

639

this.sendAlert('Database disk space critical');

640

}

641

}

642

643

sendAlert(message) {

644

console.error('DATABASE ALERT:', message);

645

// Integrate with alerting system (PagerDuty, Slack, etc.)

646

}

647

648

getMetrics() {

649

return {

650

error_counts: Object.fromEntries(this.errorCounts),

651

total_errors: Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0),

652

uptime: Date.now() - this.lastReset

653

};

654

}

655

656

reset() {

657

this.errorCounts.clear();

658

this.errorRates.clear();

659

this.lastReset = Date.now();

660

}

661

}

662

663

// Usage

664

const metrics = new DatabaseErrorMetrics();

665

666

// Wrap SQL instance to track all errors

667

const originalQuery = sql;

668

const monitoredSql = new Proxy(sql, {

669

apply: async (target, thisArg, argumentsList) => {

670

try {

671

return await target.apply(thisArg, argumentsList);

672

} catch (error) {

673

metrics.recordError(error);

674

throw error;

675

}

676

}

677

});

678

679

// Periodic metrics reporting

680

setInterval(() => {

681

console.log('Database Error Metrics:', metrics.getMetrics());

682

}, 60000); // Every minute

683

```

684

685

## Error Prevention

686

687

### Input Validation

688

689

Prevent errors through comprehensive input validation.

690

691

```javascript

692

class DatabaseValidator {

693

static validateEmail(email) {

694

if (!email || typeof email !== 'string') {

695

throw new Error('Email is required and must be a string');

696

}

697

698

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

699

if (!emailRegex.test(email)) {

700

throw new Error('Invalid email format');

701

}

702

703

if (email.length > 254) {

704

throw new Error('Email too long (max 254 characters)');

705

}

706

}

707

708

static validateId(id) {

709

if (!Number.isInteger(id) || id <= 0) {

710

throw new Error('ID must be a positive integer');

711

}

712

}

713

714

static validateRequired(value, fieldName) {

715

if (value == null || value === '') {

716

throw new Error(`${fieldName} is required`);

717

}

718

}

719

720

static validateLength(value, fieldName, maxLength) {

721

if (typeof value === 'string' && value.length > maxLength) {

722

throw new Error(`${fieldName} exceeds maximum length of ${maxLength}`);

723

}

724

}

725

}

726

727

// Usage in database operations

728

async function createUser(userData) {

729

// Validate inputs before database operation

730

DatabaseValidator.validateRequired(userData.email, 'Email');

731

DatabaseValidator.validateEmail(userData.email);

732

DatabaseValidator.validateRequired(userData.username, 'Username');

733

DatabaseValidator.validateLength(userData.username, 'Username', 50);

734

735

try {

736

const [user] = await sql`

737

INSERT INTO users (email, username, password_hash)

738

VALUES (${userData.email}, ${userData.username}, ${userData.passwordHash})

739

RETURNING *

740

`;

741

return user;

742

} catch (error) {

743

// Handle remaining database errors

744

throw new Error(`User creation failed: ${error.message}`);

745

}

746

}

747

```