or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

binary-resolution.mdcli-utilities.mddatabase-operations.mdengine-commands.mderror-handling.mdgenerators.mdindex.mdsyntax-highlighting.mdtracing.mdutilities.md

error-handling.mddocs/

0

# Error Handling and Panic Management

1

2

The error handling domain provides comprehensive error management including Rust panic recovery, WASM error handling, error reporting, and user-friendly error messaging for the Prisma ecosystem.

3

4

## Classes

5

6

### Rust Panic Error

7

8

#### `RustPanic`

9

10

Custom error class for handling Rust panics from Prisma engines with detailed context and reporting capabilities.

11

12

```typescript { .api }

13

class RustPanic extends Error {

14

readonly __typename = 'RustPanic'

15

16

constructor(

17

message: string,

18

public rustStack: string,

19

public request: any,

20

public area: ErrorArea,

21

public introspectionUrl?: string

22

)

23

}

24

```

25

26

**Properties:**

27

- `__typename: 'RustPanic'` - Type identifier for runtime checks

28

- `rustStack: string` - Rust stack trace from the panic

29

- `request: any` - Request that caused the panic

30

- `area: ErrorArea` - Error area classification

31

- `introspectionUrl?: string` - Optional introspection URL for database errors

32

33

**Constructor Parameters:**

34

- `message: string` - Human-readable error message

35

- `rustStack: string` - Raw Rust stack trace

36

- `request: any` - Original request that triggered the panic

37

- `area: ErrorArea` - Error area for categorization

38

- `introspectionUrl?: string` - Optional URL for database introspection errors

39

40

**Example:**

41

```typescript

42

const panic = new RustPanic(

43

'Query engine panicked during execution',

44

'thread \'main\' panicked at \'assertion failed: user.id > 0\'',

45

{ query: 'SELECT * FROM users WHERE id = ?', params: [-1] },

46

ErrorArea.QUERY_ENGINE_LIBRARY_CLI,

47

'postgresql://localhost:5432/mydb'

48

)

49

50

console.log(panic.message) // 'Query engine panicked during execution'

51

console.log(panic.rustStack) // Full Rust stack trace

52

console.log(panic.area) // ErrorArea.QUERY_ENGINE_LIBRARY_CLI

53

console.log(panic.introspectionUrl) // 'postgresql://localhost:5432/mydb'

54

```

55

56

## Functions

57

58

### Type Guards and Detection

59

60

#### `isRustPanic(e)`

61

62

Type guard to safely check if an error is a RustPanic instance.

63

64

```typescript { .api }

65

function isRustPanic(e: Error): e is RustPanic

66

```

67

68

**Parameters:**

69

- `e: Error` - Error instance to check

70

71

**Returns:** `e is RustPanic` - Type guard indicating if error is a RustPanic

72

73

**Example:**

74

```typescript

75

try {

76

await prisma.user.findMany()

77

} catch (error) {

78

if (isRustPanic(error)) {

79

console.error('Rust panic occurred:')

80

console.error('Area:', error.area)

81

console.error('Stack:', error.rustStack)

82

83

// Report panic for debugging

84

await sendPanic({

85

error,

86

cliVersion: '5.0.0',

87

enginesVersion: '5.0.0',

88

getDatabaseVersionSafe: async () => 'PostgreSQL 15.0'

89

})

90

} else {

91

console.error('Regular error:', error.message)

92

}

93

}

94

```

95

96

#### `isWasmPanic(error)`

97

98

Type guard to check if an error is a WASM runtime panic.

99

100

```typescript { .api }

101

function isWasmPanic(error: Error): error is WasmPanic

102

```

103

104

**Parameters:**

105

- `error: Error` - Error instance to check

106

107

**Returns:** `error is WasmPanic` - Type guard for WASM panic errors

108

109

#### `getWasmError(error)`

110

111

Extracts error information from WASM panic with structured data.

112

113

```typescript { .api }

114

function getWasmError(error: WasmPanic): { message: string; stack: string }

115

```

116

117

**Parameters:**

118

- `error: WasmPanic` - WASM panic error instance

119

120

**Returns:** `{ message: string; stack: string }` - Extracted error information

121

122

**Example:**

123

```typescript

124

try {

125

await formatSchema({ schemas })

126

} catch (error) {

127

if (isWasmPanic(error)) {

128

const { message, stack } = getWasmError(error)

129

console.error('WASM Error:', message)

130

console.error('Stack:', stack)

131

}

132

}

133

```

134

135

### Error Reporting

136

137

#### `sendPanic(options)`

138

139

Sends panic information to error reporting service for analysis and debugging.

140

141

```typescript { .api }

142

function sendPanic(options: SendPanicOptions): Promise<number>

143

```

144

145

**SendPanicOptions:**

146

```typescript { .api }

147

interface SendPanicOptions {

148

error: RustPanic // The panic error

149

cliVersion: string // CLI version string

150

enginesVersion: string // Engines version string

151

getDatabaseVersionSafe: () => Promise<string> // Function to get DB version safely

152

}

153

```

154

155

**Parameters:**

156

- `error: RustPanic` - The panic error to report

157

- `cliVersion: string` - Version of Prisma CLI

158

- `enginesVersion: string` - Version of Prisma engines

159

- `getDatabaseVersionSafe: () => Promise<string>` - Async function to safely retrieve database version

160

161

**Returns:** `Promise<number>` - Report ID for tracking

162

163

**Example:**

164

```typescript

165

async function handlePanicWithReporting(error: RustPanic) {

166

try {

167

const reportId = await sendPanic({

168

error,

169

cliVersion: process.env.PRISMA_CLI_VERSION || 'unknown',

170

enginesVersion: process.env.PRISMA_ENGINES_VERSION || 'unknown',

171

getDatabaseVersionSafe: async () => {

172

try {

173

// Safely get database version

174

const result = await prisma.$queryRaw`SELECT version()`

175

return result[0].version

176

} catch {

177

return 'unknown'

178

}

179

}

180

})

181

182

console.log(`Panic reported with ID: ${reportId}`)

183

} catch (reportError) {

184

console.warn('Failed to report panic:', reportError.message)

185

}

186

}

187

```

188

189

### Warning Management

190

191

#### `warnOnce(message)`

192

193

Warns only once for the same message to avoid spam in logs.

194

195

```typescript { .api }

196

function warnOnce(message: string): void

197

```

198

199

**Parameters:**

200

- `message: string` - Warning message to display

201

202

**Behavior:** Only prints the warning the first time it's called with a specific message

203

204

**Example:**

205

```typescript

206

function processLegacyConfig(config: any) {

207

if (config.legacyOption) {

208

warnOnce('legacyOption is deprecated and will be removed in v6.0')

209

// This warning will only appear once per process

210

}

211

}

212

213

// First call - warning shown

214

processLegacyConfig({ legacyOption: true })

215

216

// Subsequent calls - no warning

217

processLegacyConfig({ legacyOption: true })

218

processLegacyConfig({ legacyOption: true })

219

```

220

221

## Enums and Types

222

223

### Error Areas

224

225

#### `ErrorArea`

226

227

Enumeration categorizing different areas where errors can occur in the Prisma ecosystem.

228

229

```typescript { .api }

230

enum ErrorArea {

231

LIFT_CLI = 'LIFT_CLI',

232

PHOTON_STUDIO = 'PHOTON_STUDIO',

233

INTROSPECTION_CLI = 'INTROSPECTION_CLI',

234

FMT_CLI = 'FMT_CLI',

235

QUERY_ENGINE_BINARY_CLI = 'QUERY_ENGINE_BINARY_CLI',

236

QUERY_ENGINE_LIBRARY_CLI = 'QUERY_ENGINE_LIBRARY_CLI'

237

}

238

```

239

240

**Areas:**

241

- `LIFT_CLI` - CLI lift operations (legacy migrations)

242

- `PHOTON_STUDIO` - Photon Studio operations (legacy)

243

- `INTROSPECTION_CLI` - Database introspection operations (legacy)

244

- `FMT_CLI` - Schema formatting operations

245

- `QUERY_ENGINE_BINARY_CLI` - Query engine binary operations

246

- `QUERY_ENGINE_LIBRARY_CLI` - Query engine library operations

247

248

### WASM Error Types

249

250

#### `WasmPanic`

251

252

Branded type for WASM runtime errors with specific characteristics.

253

254

```typescript { .api }

255

type WasmPanic = Error & { name: 'RuntimeError' }

256

```

257

258

**Properties:**

259

- Extends standard `Error`

260

- `name: 'RuntimeError'` - Specific name for WASM panics

261

262

## Examples

263

264

### Comprehensive Error Handler

265

266

```typescript

267

import {

268

isRustPanic,

269

isWasmPanic,

270

getWasmError,

271

sendPanic,

272

warnOnce,

273

ErrorArea,

274

type RustPanic

275

} from '@prisma/internals'

276

277

interface ErrorContext {

278

operation: string

279

timestamp: Date

280

userId?: string

281

requestId?: string

282

}

283

284

class PrismaErrorHandler {

285

private reportingEnabled: boolean

286

private cliVersion: string

287

private enginesVersion: string

288

289

constructor(options: {

290

reportingEnabled?: boolean

291

cliVersion: string

292

enginesVersion: string

293

}) {

294

this.reportingEnabled = options.reportingEnabled ?? true

295

this.cliVersion = options.cliVersion

296

this.enginesVersion = options.enginesVersion

297

}

298

299

/**

300

* Handle any Prisma-related error with context

301

*/

302

async handleError(

303

error: Error,

304

context: ErrorContext

305

): Promise<{ handled: boolean; reportId?: number }> {

306

console.error(`❌ Error in ${context.operation} at ${context.timestamp.toISOString()}`)

307

308

if (context.requestId) {

309

console.error(` Request ID: ${context.requestId}`)

310

}

311

312

// Handle Rust panics

313

if (isRustPanic(error)) {

314

return await this.handleRustPanic(error, context)

315

}

316

317

// Handle WASM panics

318

if (isWasmPanic(error)) {

319

return this.handleWasmPanic(error, context)

320

}

321

322

// Handle regular errors

323

return this.handleRegularError(error, context)

324

}

325

326

/**

327

* Handle Rust panic with detailed reporting

328

*/

329

private async handleRustPanic(

330

error: RustPanic,

331

context: ErrorContext

332

): Promise<{ handled: boolean; reportId?: number }> {

333

console.error('πŸ¦€ Rust Panic Detected:')

334

console.error(` Area: ${error.area}`)

335

console.error(` Message: ${error.message}`)

336

337

// Log request details if available

338

if (error.request) {

339

console.error(' Request Details:')

340

console.error(` ${JSON.stringify(error.request, null, 2)}`)

341

}

342

343

// Show user-friendly message based on error area

344

this.showUserFriendlyPanicMessage(error.area)

345

346

// Report panic if enabled

347

let reportId: number | undefined

348

if (this.reportingEnabled) {

349

try {

350

reportId = await sendPanic({

351

error,

352

cliVersion: this.cliVersion,

353

enginesVersion: this.enginesVersion,

354

getDatabaseVersionSafe: async () => {

355

try {

356

// Attempt to get database version safely

357

return await this.getDatabaseVersionSafe()

358

} catch {

359

return 'unknown'

360

}

361

}

362

})

363

364

console.error(` Panic reported with ID: ${reportId}`)

365

} catch (reportError) {

366

console.warn(` Failed to report panic: ${reportError.message}`)

367

}

368

}

369

370

return { handled: true, reportId }

371

}

372

373

/**

374

* Handle WASM panic

375

*/

376

private handleWasmPanic(

377

error: WasmPanic,

378

context: ErrorContext

379

): { handled: boolean } {

380

console.error('πŸ•ΈοΈ WASM Panic Detected:')

381

382

const { message, stack } = getWasmError(error)

383

console.error(` Message: ${message}`)

384

console.error(` Stack: ${stack}`)

385

386

// Show troubleshooting advice

387

console.error('\nπŸ’‘ Troubleshooting WASM errors:')

388

console.error(' β€’ Try updating to the latest Prisma version')

389

console.error(' β€’ Check if your schema is valid')

390

console.error(' β€’ Restart your development server')

391

392

return { handled: true }

393

}

394

395

/**

396

* Handle regular errors

397

*/

398

private handleRegularError(

399

error: Error,

400

context: ErrorContext

401

): { handled: boolean } {

402

console.error('⚠️ Regular Error:')

403

console.error(` Message: ${error.message}`)

404

405

if (error.stack) {

406

console.error(` Stack: ${error.stack}`)

407

}

408

409

// Check for common error patterns

410

if (error.message.includes('ECONNREFUSED')) {

411

console.error('\nπŸ’‘ Connection refused - check if your database is running')

412

} else if (error.message.includes('authentication failed')) {

413

console.error('\nπŸ’‘ Authentication failed - check your database credentials')

414

} else if (error.message.includes('relation') && error.message.includes('does not exist')) {

415

console.error('\nπŸ’‘ Database table missing - run `prisma db push` or migrations')

416

}

417

418

return { handled: true }

419

}

420

421

/**

422

* Show user-friendly message based on panic area

423

*/

424

private showUserFriendlyPanicMessage(area: ErrorArea): void {

425

const messages = {

426

[ErrorArea.QUERY_ENGINE_BINARY_CLI]: 'Query engine crashed during database operation',

427

[ErrorArea.QUERY_ENGINE_LIBRARY_CLI]: 'Query engine library encountered an internal error',

428

[ErrorArea.FMT_CLI]: 'Schema formatter crashed while processing your schema',

429

[ErrorArea.INTROSPECTION_CLI]: 'Database introspection failed unexpectedly',

430

[ErrorArea.LIFT_CLI]: 'Migration engine encountered an error',

431

[ErrorArea.PHOTON_STUDIO]: 'Prisma Studio encountered an internal error'

432

}

433

434

const message = messages[area] || 'Prisma encountered an unexpected error'

435

console.error(`\nπŸ“ ${message}`)

436

437

console.error('\nπŸ”§ Next steps:')

438

console.error(' β€’ Check your database connection')

439

console.error(' β€’ Verify your schema syntax')

440

console.error(' β€’ Update to the latest Prisma version')

441

console.error(' β€’ Report this issue if it persists')

442

}

443

444

/**

445

* Safely get database version

446

*/

447

private async getDatabaseVersionSafe(): Promise<string> {

448

// Implementation would depend on having access to PrismaClient

449

// This is a placeholder

450

return 'PostgreSQL 15.0'

451

}

452

}

453

```

454

455

### Error Recovery and Retry Logic

456

457

```typescript

458

import {

459

isRustPanic,

460

isWasmPanic,

461

warnOnce,

462

type RustPanic

463

} from '@prisma/internals'

464

465

interface RetryOptions {

466

maxAttempts: number

467

baseDelay: number

468

maxDelay: number

469

shouldRetry?: (error: Error, attempt: number) => boolean

470

}

471

472

class ErrorRecoveryManager {

473

474

/**

475

* Execute operation with retry logic and error recovery

476

*/

477

async withRetry<T>(

478

operation: () => Promise<T>,

479

options: RetryOptions

480

): Promise<T> {

481

const { maxAttempts, baseDelay, maxDelay, shouldRetry } = options

482

let lastError: Error | undefined

483

484

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

485

try {

486

return await operation()

487

} catch (error) {

488

lastError = error as Error

489

490

// Don't retry on panics - they indicate serious issues

491

if (isRustPanic(error) || isWasmPanic(error)) {

492

console.error(`πŸ’₯ Panic on attempt ${attempt} - not retrying`)

493

throw error

494

}

495

496

// Check custom retry logic

497

if (shouldRetry && !shouldRetry(error, attempt)) {

498

console.error(`🚫 Custom retry logic rejected attempt ${attempt}`)

499

throw error

500

}

501

502

// Don't retry on last attempt

503

if (attempt === maxAttempts) {

504

console.error(`πŸ”„ Max attempts (${maxAttempts}) reached`)

505

throw error

506

}

507

508

// Calculate delay with exponential backoff

509

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

510

511

console.warn(`⏳ Attempt ${attempt} failed, retrying in ${delay}ms...`)

512

console.warn(` Error: ${error.message}`)

513

514

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

515

}

516

}

517

518

throw lastError || new Error('Operation failed after retries')

519

}

520

521

/**

522

* Graceful degradation for non-critical operations

523

*/

524

async withFallback<T>(

525

primaryOperation: () => Promise<T>,

526

fallbackOperation: () => Promise<T>,

527

context: string

528

): Promise<T> {

529

try {

530

return await primaryOperation()

531

} catch (error) {

532

if (isRustPanic(error)) {

533

warnOnce(`Rust panic in ${context} - using fallback`)

534

console.warn(`Primary operation failed due to panic: ${error.message}`)

535

} else if (isWasmPanic(error)) {

536

warnOnce(`WASM panic in ${context} - using fallback`)

537

console.warn(`Primary operation failed due to WASM error`)

538

} else {

539

console.warn(`Primary operation failed in ${context}: ${error.message}`)

540

}

541

542

console.log(`πŸ”„ Attempting fallback for ${context}...`)

543

return await fallbackOperation()

544

}

545

}

546

}

547

548

// Usage examples

549

const recoveryManager = new ErrorRecoveryManager()

550

551

// Database operation with retry

552

const users = await recoveryManager.withRetry(

553

async () => {

554

return await prisma.user.findMany()

555

},

556

{

557

maxAttempts: 3,

558

baseDelay: 1000,

559

maxDelay: 5000,

560

shouldRetry: (error, attempt) => {

561

// Don't retry validation errors

562

return !error.message.includes('Invalid')

563

}

564

}

565

)

566

567

// Schema operation with fallback

568

const formattedSchema = await recoveryManager.withFallback(

569

async () => {

570

// Try WASM formatter

571

return await formatSchema({ schemas })

572

},

573

async () => {

574

// Fallback to simple string formatting

575

warnOnce('Using simple schema formatting as fallback')

576

return schemas // Return unformatted

577

},

578

'schema formatting'

579

)

580

```

581

582

### Error Monitoring and Analytics

583

584

```typescript

585

import {

586

isRustPanic,

587

isWasmPanic,

588

getWasmError,

589

ErrorArea,

590

type RustPanic

591

} from '@prisma/internals'

592

593

interface ErrorMetrics {

594

totalErrors: number

595

rustPanics: number

596

wasmPanics: number

597

regularErrors: number

598

errorsByArea: Record<ErrorArea, number>

599

commonErrors: Record<string, number>

600

}

601

602

class ErrorMonitor {

603

private metrics: ErrorMetrics

604

private errorHistory: Array<{

605

error: Error

606

timestamp: Date

607

context: string

608

}> = []

609

610

constructor() {

611

this.metrics = {

612

totalErrors: 0,

613

rustPanics: 0,

614

wasmPanics: 0,

615

regularErrors: 0,

616

errorsByArea: {} as Record<ErrorArea, number>,

617

commonErrors: {}

618

}

619

}

620

621

/**

622

* Record error for monitoring

623

*/

624

recordError(error: Error, context: string): void {

625

this.metrics.totalErrors++

626

this.errorHistory.push({

627

error,

628

timestamp: new Date(),

629

context

630

})

631

632

if (isRustPanic(error)) {

633

this.metrics.rustPanics++

634

this.recordPanicArea(error.area)

635

} else if (isWasmPanic(error)) {

636

this.metrics.wasmPanics++

637

} else {

638

this.metrics.regularErrors++

639

}

640

641

// Track common error patterns

642

const errorKey = this.categorizeError(error)

643

this.metrics.commonErrors[errorKey] = (this.metrics.commonErrors[errorKey] || 0) + 1

644

645

// Limit history size

646

if (this.errorHistory.length > 1000) {

647

this.errorHistory = this.errorHistory.slice(-500)

648

}

649

}

650

651

private recordPanicArea(area: ErrorArea): void {

652

this.metrics.errorsByArea[area] = (this.metrics.errorsByArea[area] || 0) + 1

653

}

654

655

private categorizeError(error: Error): string {

656

const message = error.message.toLowerCase()

657

658

if (message.includes('connection')) return 'connection_error'

659

if (message.includes('authentication')) return 'auth_error'

660

if (message.includes('timeout')) return 'timeout_error'

661

if (message.includes('syntax')) return 'syntax_error'

662

if (message.includes('permission')) return 'permission_error'

663

if (message.includes('not found')) return 'not_found_error'

664

665

return 'other_error'

666

}

667

668

/**

669

* Generate error report

670

*/

671

generateReport(): string {

672

const { metrics } = this

673

674

let report = '\nπŸ“Š Error Monitoring Report\n'

675

report += '═'.repeat(40) + '\n\n'

676

677

report += `πŸ“ˆ Overall Statistics:\n`

678

report += ` Total Errors: ${metrics.totalErrors}\n`

679

report += ` Rust Panics: ${metrics.rustPanics}\n`

680

report += ` WASM Panics: ${metrics.wasmPanics}\n`

681

report += ` Regular Errors: ${metrics.regularErrors}\n\n`

682

683

if (metrics.rustPanics > 0) {

684

report += `πŸ¦€ Panics by Area:\n`

685

for (const [area, count] of Object.entries(metrics.errorsByArea)) {

686

report += ` ${area}: ${count}\n`

687

}

688

report += '\n'

689

}

690

691

if (Object.keys(metrics.commonErrors).length > 0) {

692

report += `πŸ“‹ Common Errors:\n`

693

const sortedErrors = Object.entries(metrics.commonErrors)

694

.sort(([,a], [,b]) => b - a)

695

.slice(0, 5)

696

697

for (const [category, count] of sortedErrors) {

698

report += ` ${category.replace('_', ' ')}: ${count}\n`

699

}

700

report += '\n'

701

}

702

703

// Recent errors

704

const recentErrors = this.errorHistory.slice(-5)

705

if (recentErrors.length > 0) {

706

report += `πŸ• Recent Errors:\n`

707

for (const { error, timestamp, context } of recentErrors) {

708

const timeStr = timestamp.toISOString().substring(11, 19)

709

report += ` ${timeStr} [${context}] ${error.message.substring(0, 50)}...\n`

710

}

711

}

712

713

return report

714

}

715

716

/**

717

* Check if error rate is concerning

718

*/

719

getHealthStatus(): {

720

status: 'healthy' | 'warning' | 'critical'

721

message: string

722

} {

723

const recentErrors = this.errorHistory.filter(

724

entry => Date.now() - entry.timestamp.getTime() < 5 * 60 * 1000 // Last 5 minutes

725

)

726

727

const panicRate = this.metrics.rustPanics / Math.max(this.metrics.totalErrors, 1)

728

729

if (recentErrors.length > 10) {

730

return {

731

status: 'critical',

732

message: `High error rate: ${recentErrors.length} errors in last 5 minutes`

733

}

734

}

735

736

if (panicRate > 0.1) {

737

return {

738

status: 'critical',

739

message: `High panic rate: ${(panicRate * 100).toFixed(1)}% of errors are panics`

740

}

741

}

742

743

if (recentErrors.length > 5 || this.metrics.totalErrors > 20) {

744

return {

745

status: 'warning',

746

message: 'Elevated error levels detected'

747

}

748

}

749

750

return {

751

status: 'healthy',

752

message: 'Error levels within normal range'

753

}

754

}

755

}

756

```