or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

callbacks.mdclustering.mdconfiguration.mdconnections.mderrors.mdindex.mdpooling.mdqueries.mdtypes.md

errors.mddocs/

0

# Error Handling

1

2

Structured error handling with detailed SQL error information, error codes, and connection state management for robust application development. The MariaDB connector provides comprehensive error reporting and recovery mechanisms.

3

4

## Capabilities

5

6

### SqlError Interface

7

8

Main error class for all database-related errors with detailed diagnostic information.

9

10

```typescript { .api }

11

interface SqlError extends Error {

12

/** Error name (always 'SqlError') */

13

name: string;

14

15

/** Human-readable error message */

16

message: string;

17

18

/** Original SQL error message from server */

19

sqlMessage: string | null;

20

21

/** Deprecated alias for sqlMessage */

22

text: string | null;

23

24

/** SQL command that caused the error */

25

sql: string | null;

26

27

/** MySQL/MariaDB error number */

28

errno: number;

29

30

/** SQL state code (SQLSTATE) */

31

sqlState?: string | null;

32

33

/** Error code string */

34

code: string | null;

35

36

/** Whether error is fatal to connection */

37

fatal: boolean;

38

}

39

```

40

41

### SqlError Constructor

42

43

Constructor for creating SqlError instances with detailed error information.

44

45

```typescript { .api }

46

interface SqlErrorConstructor extends ErrorConstructor {

47

new (

48

msg: string,

49

sql?: string,

50

fatal?: boolean,

51

info?: { threadId?: number },

52

sqlState?: string | null,

53

errno?: number,

54

additionalStack?: string,

55

addHeader?: boolean,

56

cause?: unknown

57

): SqlError;

58

59

readonly prototype: SqlError;

60

}

61

62

declare const SqlError: SqlErrorConstructor;

63

```

64

65

### Error Types and Categories

66

67

Common error categories and their characteristics:

68

69

**Connection Errors:**

70

- `ER_CONNECTION_ALREADY_CLOSED` (45001) - Connection was already closed

71

- `ER_SOCKET_UNEXPECTED_CLOSE` (45007) - Socket closed unexpectedly

72

- `ER_SOCKET_TIMEOUT` (45009) - Socket operation timed out

73

- `ER_PING_TIMEOUT` (45010) - Connection ping timed out

74

75

**Authentication Errors:**

76

- `ER_AUTHENTICATION_PLUGIN_NOT_SUPPORTED` (45016) - Auth plugin not supported

77

- `ER_AUTHENTICATION_BAD_PACKET` (45017) - Bad authentication packet

78

- `ER_AUTHENTICATION_PLUGIN_ERR` (45018) - Authentication plugin error

79

80

**Pool Errors:**

81

- `ER_POOL_ALREADY_CLOSED` (45011) - Pool was already closed

82

- `ER_GET_CONNECTION_TIMEOUT` (45012) - Connection acquisition timeout

83

- `ER_POOL_CONNECTION_CLOSED` (45013) - Pool connection was closed

84

85

**Protocol Errors:**

86

- `ER_UNEXPECTED_PACKET` (45019) - Unexpected packet received

87

- `ER_BAD_PARAMETER_VALUE` (45020) - Invalid parameter value

88

- `ER_LOCAL_INFILE_DISABLED` (45022) - LOAD LOCAL INFILE disabled

89

90

### Error Handling Patterns

91

92

```typescript

93

// Basic try-catch error handling

94

try {

95

const result = await connection.query("SELECT * FROM users WHERE id = ?", [userId]);

96

return result;

97

} catch (error) {

98

if (error instanceof SqlError) {

99

console.error('SQL Error:', {

100

code: error.code,

101

errno: error.errno,

102

sqlState: error.sqlState,

103

message: error.sqlMessage,

104

sql: error.sql,

105

fatal: error.fatal

106

});

107

108

// Handle specific error types

109

if (error.errno === 1062) { // Duplicate entry

110

throw new Error('User already exists');

111

} else if (error.errno === 1054) { // Unknown column

112

throw new Error('Invalid column name in query');

113

}

114

}

115

throw error;

116

}

117

```

118

119

### Error Classification and Handling

120

121

```typescript

122

class DatabaseService {

123

private isRetryableError(error: SqlError): boolean {

124

// Network/connection errors that can be retried

125

const retryableCodes = [

126

'ECONNRESET',

127

'ENOTFOUND',

128

'ETIMEDOUT',

129

'ECONNREFUSED'

130

];

131

132

const retryableErrno = [

133

2013, // Lost connection during query

134

2006, // MySQL server has gone away

135

1205, // Lock wait timeout

136

1213 // Deadlock found

137

];

138

139

return retryableCodes.includes(error.code || '') ||

140

retryableErrno.includes(error.errno);

141

}

142

143

private isConnectionError(error: SqlError): boolean {

144

return error.fatal ||

145

error.errno >= 45001 && error.errno <= 45010;

146

}

147

148

async executeWithRetry<T>(operation: () => Promise<T>, maxRetries = 3): Promise<T> {

149

let lastError: SqlError;

150

151

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

152

try {

153

return await operation();

154

} catch (error) {

155

lastError = error as SqlError;

156

157

if (!this.isRetryableError(lastError) || attempt === maxRetries) {

158

throw lastError;

159

}

160

161

console.warn(`Attempt ${attempt} failed, retrying:`, lastError.message);

162

await this.delay(Math.pow(2, attempt) * 1000); // Exponential backoff

163

}

164

}

165

166

throw lastError!;

167

}

168

169

private delay(ms: number): Promise<void> {

170

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

171

}

172

}

173

```

174

175

### Connection Error Recovery

176

177

```typescript

178

class ConnectionManager {

179

private connection: Connection | null = null;

180

private pool: Pool;

181

182

constructor(config: PoolConfig) {

183

this.pool = mariadb.createPool(config);

184

185

// Handle pool errors

186

this.pool.on('error', (err: SqlError) => {

187

console.error('Pool error:', err);

188

189

if (this.isPoolFatalError(err)) {

190

this.recreatePool();

191

}

192

});

193

}

194

195

private isPoolFatalError(error: SqlError): boolean {

196

return error.errno === 45011; // ER_POOL_ALREADY_CLOSED

197

}

198

199

private async recreatePool(): Promise<void> {

200

try {

201

await this.pool.end();

202

} catch (error) {

203

console.warn('Error closing old pool:', error);

204

}

205

206

this.pool = mariadb.createPool(this.pool.config);

207

}

208

209

async getConnection(): Promise<PoolConnection> {

210

try {

211

return await this.pool.getConnection();

212

} catch (error) {

213

if (error instanceof SqlError && error.errno === 45012) {

214

// Connection acquisition timeout

215

throw new Error('Database is overloaded - try again later');

216

}

217

throw error;

218

}

219

}

220

}

221

```

222

223

### Transaction Error Handling

224

225

```typescript

226

async function safeTransaction<T>(

227

connection: Connection,

228

operation: (conn: Connection) => Promise<T>

229

): Promise<T> {

230

const savepoint = `sp_${Date.now()}_${Math.random().toString(36).substring(2)}`;

231

232

try {

233

await connection.beginTransaction();

234

235

// Create savepoint for nested error handling

236

await connection.query(`SAVEPOINT ${savepoint}`);

237

238

const result = await operation(connection);

239

240

await connection.commit();

241

return result;

242

243

} catch (error) {

244

try {

245

if (error instanceof SqlError) {

246

if (error.errno === 1213) { // Deadlock

247

console.warn('Deadlock detected, rolling back transaction');

248

await connection.rollback();

249

throw new Error('Transaction failed due to deadlock - please retry');

250

} else if (error.errno === 1205) { // Lock timeout

251

console.warn('Lock timeout, rolling back to savepoint');

252

await connection.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);

253

throw new Error('Transaction timed out waiting for lock');

254

} else {

255

// General rollback

256

await connection.rollback();

257

}

258

} else {

259

await connection.rollback();

260

}

261

} catch (rollbackError) {

262

console.error('Error during rollback:', rollbackError);

263

// Connection may be in bad state

264

if (connection.isValid()) {

265

try {

266

await connection.reset();

267

} catch (resetError) {

268

console.error('Error resetting connection:', resetError);

269

}

270

}

271

}

272

273

throw error;

274

}

275

}

276

```

277

278

### Logging and Monitoring Integration

279

280

```typescript

281

class DatabaseLogger {

282

private logger: any; // Your logging library

283

284

constructor(logger: any) {

285

this.logger = logger;

286

}

287

288

logError(error: SqlError, context?: any): void {

289

const errorData = {

290

type: 'sql_error',

291

code: error.code,

292

errno: error.errno,

293

sqlState: error.sqlState,

294

message: error.sqlMessage,

295

sql: error.sql,

296

fatal: error.fatal,

297

context: context

298

};

299

300

if (error.fatal || this.isCriticalError(error)) {

301

this.logger.error('Critical database error', errorData);

302

// Send alert to monitoring system

303

this.sendAlert('critical_db_error', errorData);

304

} else {

305

this.logger.warn('Database error', errorData);

306

}

307

}

308

309

private isCriticalError(error: SqlError): boolean {

310

const criticalErrorNumbers = [

311

1040, // Too many connections

312

1203, // User has more than max_user_connections

313

1226, // User has exceeded max_updates_per_hour

314

2006, // MySQL server has gone away

315

2013 // Lost connection during query

316

];

317

318

return criticalErrorNumbers.includes(error.errno);

319

}

320

321

private sendAlert(type: string, data: any): void {

322

// Integration with monitoring system

323

// e.g., Datadog, New Relic, custom alerting

324

}

325

}

326

```

327

328

### Custom Error Classes

329

330

```typescript

331

// Application-specific error classes

332

class DatabaseError extends Error {

333

constructor(

334

message: string,

335

public readonly originalError: SqlError,

336

public readonly operation: string

337

) {

338

super(message);

339

this.name = 'DatabaseError';

340

}

341

342

get isRetryable(): boolean {

343

return [1205, 1213, 2006, 2013].includes(this.originalError.errno);

344

}

345

346

get isConnectionIssue(): boolean {

347

return this.originalError.fatal ||

348

this.originalError.errno >= 45001 && this.originalError.errno <= 45010;

349

}

350

}

351

352

class ValidationError extends DatabaseError {

353

constructor(originalError: SqlError, field?: string) {

354

const message = field

355

? `Validation failed for field '${field}': ${originalError.sqlMessage}`

356

: `Validation failed: ${originalError.sqlMessage}`;

357

358

super(message, originalError, 'validation');

359

this.name = 'ValidationError';

360

}

361

}

362

363

class DuplicateEntryError extends DatabaseError {

364

constructor(originalError: SqlError, duplicateKey?: string) {

365

const message = duplicateKey

366

? `Duplicate entry for key '${duplicateKey}'`

367

: 'Duplicate entry detected';

368

369

super(message, originalError, 'insert');

370

this.name = 'DuplicateEntryError';

371

}

372

}

373

374

// Error factory

375

function createApplicationError(sqlError: SqlError, operation: string): DatabaseError {

376

switch (sqlError.errno) {

377

case 1062: // Duplicate entry

378

return new DuplicateEntryError(sqlError);

379

case 1452: // Foreign key constraint fails

380

case 1048: // Column cannot be null

381

case 1406: // Data too long for column

382

return new ValidationError(sqlError);

383

default:

384

return new DatabaseError(sqlError.sqlMessage || sqlError.message, sqlError, operation);

385

}

386

}

387

```

388

389

### Error Recovery Strategies

390

391

```typescript

392

class ResilientDatabaseService {

393

private pool: Pool;

394

private circuitBreaker: CircuitBreaker;

395

396

constructor(config: PoolConfig) {

397

this.pool = mariadb.createPool(config);

398

this.circuitBreaker = new CircuitBreaker({

399

failureThreshold: 5,

400

resetTimeout: 30000

401

});

402

}

403

404

async query<T>(sql: string, values?: any): Promise<T> {

405

return this.circuitBreaker.execute(async () => {

406

try {

407

return await this.pool.query(sql, values);

408

} catch (error) {

409

if (error instanceof SqlError) {

410

// Transform to application error

411

throw createApplicationError(error, 'query');

412

}

413

throw error;

414

}

415

});

416

}

417

418

async healthCheck(): Promise<boolean> {

419

try {

420

await this.pool.query('SELECT 1');

421

return true;

422

} catch (error) {

423

console.error('Database health check failed:', error);

424

return false;

425

}

426

}

427

}

428

429

// Simple circuit breaker implementation

430

class CircuitBreaker {

431

private failures = 0;

432

private lastFailureTime = 0;

433

private state: 'closed' | 'open' | 'half-open' = 'closed';

434

435

constructor(

436

private options: {

437

failureThreshold: number;

438

resetTimeout: number;

439

}

440

) {}

441

442

async execute<T>(operation: () => Promise<T>): Promise<T> {

443

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

444

if (Date.now() - this.lastFailureTime > this.options.resetTimeout) {

445

this.state = 'half-open';

446

} else {

447

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

448

}

449

}

450

451

try {

452

const result = await operation();

453

this.onSuccess();

454

return result;

455

} catch (error) {

456

this.onFailure();

457

throw error;

458

}

459

}

460

461

private onSuccess(): void {

462

this.failures = 0;

463

this.state = 'closed';

464

}

465

466

private onFailure(): void {

467

this.failures++;

468

this.lastFailureTime = Date.now();

469

470

if (this.failures >= this.options.failureThreshold) {

471

this.state = 'open';

472

}

473

}

474

}

475

```

476

477

### Common Error Codes Reference

478

479

```typescript

480

// MySQL/MariaDB Standard Error Codes

481

const MYSQL_ERRORS = {

482

// Constraint violations

483

DUPLICATE_ENTRY: 1062,

484

FOREIGN_KEY_CONSTRAINT: 1452,

485

COLUMN_CANNOT_BE_NULL: 1048,

486

DATA_TOO_LONG: 1406,

487

488

// Connection issues

489

TOO_MANY_CONNECTIONS: 1040,

490

ACCESS_DENIED: 1045,

491

UNKNOWN_DATABASE: 1049,

492

TABLE_DOESNT_EXIST: 1146,

493

COLUMN_DOESNT_EXIST: 1054,

494

495

// Lock and transaction issues

496

LOCK_WAIT_TIMEOUT: 1205,

497

DEADLOCK_FOUND: 1213,

498

499

// Server issues

500

SERVER_GONE_AWAY: 2006,

501

LOST_CONNECTION: 2013,

502

SERVER_SHUTDOWN: 1053

503

} as const;

504

505

// MariaDB Connector Specific Error Codes (45001-45062)

506

const CONNECTOR_ERRORS = {

507

// Connection Management (45001-45010)

508

CONNECTION_ALREADY_CLOSED: 45001,

509

MYSQL_CHANGE_USER_BUG: 45003,

510

CMD_NOT_EXECUTED_DESTROYED: 45004,

511

ADD_CONNECTION_CANCELLED: 45005,

512

CONN_PVT_KEY_ENCRYPTED: 45006,

513

SOCKET_UNEXPECTED_CLOSE: 45007,

514

SOCKET_BAD_PORT: 45008,

515

SOCKET_TIMEOUT: 45009,

516

PING_TIMEOUT: 45010,

517

518

// Pool Management (45011-45013)

519

POOL_ALREADY_CLOSED: 45011,

520

GET_CONNECTION_TIMEOUT: 45012,

521

POOL_CONNECTION_CLOSED: 45013,

522

523

// File Operations (45014-45015)

524

FILE_NOT_EXISTS: 45014,

525

FILE_EMPTY: 45015,

526

527

// Authentication (45016-45018)

528

AUTHENTICATION_PLUGIN_NOT_SUPPORTED: 45016,

529

AUTHENTICATION_BAD_PACKET: 45017,

530

AUTHENTICATION_PLUGIN_ERR: 45018,

531

532

// Protocol Errors (45019-45062)

533

UNEXPECTED_PACKET: 45019,

534

BAD_PARAMETER_VALUE: 45020,

535

BUFFER_PARAMETER_CONVERSION: 45021,

536

LOCAL_INFILE_DISABLED: 45022,

537

LOCAL_INFILE_NOT_READABLE: 45023,

538

PARAMETER_UNDEFINED: 45024,

539

PARAMETER_NOT_DEFINED_STMT: 45025,

540

PARAMETER_NOT_DEFINED_BULK: 45026,

541

NOT_IMPLEMENTED_FORMAT: 45027,

542

WRONG_PARAMETER_FORMAT: 45028,

543

NOT_SUPPORTED_AUTH: 45029,

544

SOCKET_DESTROYED: 45030,

545

SOCKET_NOT_WRITABLE: 45031,

546

AUTHENTICATION_SWITCH_PLUGIN: 45032,

547

AUTHENTICATION_WRONG_METHOD: 45033,

548

AUTHENTICATION_WRONG_RESPONSE: 45034,

549

AUTHENTICATION_EMPTY_RESPONSE: 45035,

550

AUTHENTICATION_FAIL_RESPONSE: 45036,

551

CHANGE_USER_RESET_PREPARE: 45037,

552

AUTHENTICATION_TRY_OTHER: 45038,

553

SOCKET_NO_CONNECTION: 45039,

554

HANDSHAKE_UNEXPECTED_PACKET: 45040,

555

HANDSHAKE_FAIL_SSL: 45041,

556

CONN_PVT_KEY_INCORRECT_PASSPHRASE: 45042,

557

STREAM_PARAMETER_NOT_READABLE: 45043,

558

STREAM_PARAMETER_NOT_ITERABLE: 45044,

559

COMMAND_NOT_EXECUTABLE: 45045,

560

COMMAND_CLOSED: 45046,

561

EMPTY_PASSWORD: 45047,

562

AUTHENTICATION_KERBEROS_PRINCIPAL: 45048,

563

AUTHENTICATION_KERBEROS_SERVICE: 45049,

564

AUTHENTICATION_KERBEROS_TARGET: 45050,

565

AUTHENTICATION_KERBEROS_TIMEOUT: 45051,

566

QUERY_INTERRUPTED: 45052,

567

QUERY_TIMEOUT: 45053,

568

MAX_ALLOWED_PACKET: 45054,

569

BATCH_WITH_NO_VALUES: 45055,

570

BATCH_BULK_NO_BATCH: 45056,

571

BATCH_WRONG_PARAMETER_NUMBER: 45057,

572

BATCH_VALIDATION_ERROR: 45058,

573

BATCH_WITHOUT_PARAMETERS: 45059,

574

CLUSTER_NO_NODES: 45060,

575

CLUSTER_NODE_NOT_FOUND: 45061,

576

CLUSTER_ALL_NODES_FAILED: 45062

577

} as const;

578

```