CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-prisma--internals

Comprehensive internal utility library for Prisma's CLI operations, schema management, generator handling, and engine interactions

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

error-handling.mddocs/

Error Handling and Panic Management

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.

Classes

Rust Panic Error

RustPanic

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

class RustPanic extends Error {
  readonly __typename = 'RustPanic'
  
  constructor(
    message: string,
    public rustStack: string,
    public request: any,
    public area: ErrorArea,
    public introspectionUrl?: string
  )
}

Properties:

  • __typename: 'RustPanic' - Type identifier for runtime checks
  • rustStack: string - Rust stack trace from the panic
  • request: any - Request that caused the panic
  • area: ErrorArea - Error area classification
  • introspectionUrl?: string - Optional introspection URL for database errors

Constructor Parameters:

  • message: string - Human-readable error message
  • rustStack: string - Raw Rust stack trace
  • request: any - Original request that triggered the panic
  • area: ErrorArea - Error area for categorization
  • introspectionUrl?: string - Optional URL for database introspection errors

Example:

const panic = new RustPanic(
  'Query engine panicked during execution',
  'thread \'main\' panicked at \'assertion failed: user.id > 0\'',
  { query: 'SELECT * FROM users WHERE id = ?', params: [-1] },
  ErrorArea.QUERY_ENGINE_LIBRARY_CLI,
  'postgresql://localhost:5432/mydb'
)

console.log(panic.message)           // 'Query engine panicked during execution'
console.log(panic.rustStack)         // Full Rust stack trace
console.log(panic.area)             // ErrorArea.QUERY_ENGINE_LIBRARY_CLI
console.log(panic.introspectionUrl) // 'postgresql://localhost:5432/mydb'

Functions

Type Guards and Detection

isRustPanic(e)

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

function isRustPanic(e: Error): e is RustPanic

Parameters:

  • e: Error - Error instance to check

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

Example:

try {
  await prisma.user.findMany()
} catch (error) {
  if (isRustPanic(error)) {
    console.error('Rust panic occurred:')
    console.error('Area:', error.area)
    console.error('Stack:', error.rustStack)
    
    // Report panic for debugging
    await sendPanic({
      error,
      cliVersion: '5.0.0',
      enginesVersion: '5.0.0',
      getDatabaseVersionSafe: async () => 'PostgreSQL 15.0'
    })
  } else {
    console.error('Regular error:', error.message)
  }
}

isWasmPanic(error)

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

function isWasmPanic(error: Error): error is WasmPanic

Parameters:

  • error: Error - Error instance to check

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

getWasmError(error)

Extracts error information from WASM panic with structured data.

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

Parameters:

  • error: WasmPanic - WASM panic error instance

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

Example:

try {
  await formatSchema({ schemas })
} catch (error) {
  if (isWasmPanic(error)) {
    const { message, stack } = getWasmError(error)
    console.error('WASM Error:', message)
    console.error('Stack:', stack)
  }
}

Error Reporting

sendPanic(options)

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

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

SendPanicOptions:

interface SendPanicOptions {
  error: RustPanic                                    // The panic error
  cliVersion: string                                  // CLI version string
  enginesVersion: string                              // Engines version string
  getDatabaseVersionSafe: () => Promise<string>       // Function to get DB version safely
}

Parameters:

  • error: RustPanic - The panic error to report
  • cliVersion: string - Version of Prisma CLI
  • enginesVersion: string - Version of Prisma engines
  • getDatabaseVersionSafe: () => Promise<string> - Async function to safely retrieve database version

Returns: Promise<number> - Report ID for tracking

Example:

async function handlePanicWithReporting(error: RustPanic) {
  try {
    const reportId = await sendPanic({
      error,
      cliVersion: process.env.PRISMA_CLI_VERSION || 'unknown',
      enginesVersion: process.env.PRISMA_ENGINES_VERSION || 'unknown',
      getDatabaseVersionSafe: async () => {
        try {
          // Safely get database version
          const result = await prisma.$queryRaw`SELECT version()`
          return result[0].version
        } catch {
          return 'unknown'
        }
      }
    })
    
    console.log(`Panic reported with ID: ${reportId}`)
  } catch (reportError) {
    console.warn('Failed to report panic:', reportError.message)
  }
}

Warning Management

warnOnce(message)

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

function warnOnce(message: string): void

Parameters:

  • message: string - Warning message to display

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

Example:

function processLegacyConfig(config: any) {
  if (config.legacyOption) {
    warnOnce('legacyOption is deprecated and will be removed in v6.0')
    // This warning will only appear once per process
  }
}

// First call - warning shown
processLegacyConfig({ legacyOption: true })

// Subsequent calls - no warning
processLegacyConfig({ legacyOption: true })
processLegacyConfig({ legacyOption: true })

Enums and Types

Error Areas

ErrorArea

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

enum ErrorArea {
  LIFT_CLI = 'LIFT_CLI',
  PHOTON_STUDIO = 'PHOTON_STUDIO',
  INTROSPECTION_CLI = 'INTROSPECTION_CLI',
  FMT_CLI = 'FMT_CLI',
  QUERY_ENGINE_BINARY_CLI = 'QUERY_ENGINE_BINARY_CLI',
  QUERY_ENGINE_LIBRARY_CLI = 'QUERY_ENGINE_LIBRARY_CLI'
}

Areas:

  • LIFT_CLI - CLI lift operations (legacy migrations)
  • PHOTON_STUDIO - Photon Studio operations (legacy)
  • INTROSPECTION_CLI - Database introspection operations (legacy)
  • FMT_CLI - Schema formatting operations
  • QUERY_ENGINE_BINARY_CLI - Query engine binary operations
  • QUERY_ENGINE_LIBRARY_CLI - Query engine library operations

WASM Error Types

WasmPanic

Branded type for WASM runtime errors with specific characteristics.

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

Properties:

  • Extends standard Error
  • name: 'RuntimeError' - Specific name for WASM panics

Examples

Comprehensive Error Handler

import {
  isRustPanic,
  isWasmPanic,
  getWasmError,
  sendPanic,
  warnOnce,
  ErrorArea,
  type RustPanic
} from '@prisma/internals'

interface ErrorContext {
  operation: string
  timestamp: Date
  userId?: string
  requestId?: string
}

class PrismaErrorHandler {
  private reportingEnabled: boolean
  private cliVersion: string
  private enginesVersion: string
  
  constructor(options: {
    reportingEnabled?: boolean
    cliVersion: string
    enginesVersion: string
  }) {
    this.reportingEnabled = options.reportingEnabled ?? true
    this.cliVersion = options.cliVersion
    this.enginesVersion = options.enginesVersion
  }
  
  /**
   * Handle any Prisma-related error with context
   */
  async handleError(
    error: Error,
    context: ErrorContext
  ): Promise<{ handled: boolean; reportId?: number }> {
    console.error(`❌ Error in ${context.operation} at ${context.timestamp.toISOString()}`)
    
    if (context.requestId) {
      console.error(`   Request ID: ${context.requestId}`)
    }
    
    // Handle Rust panics
    if (isRustPanic(error)) {
      return await this.handleRustPanic(error, context)
    }
    
    // Handle WASM panics
    if (isWasmPanic(error)) {
      return this.handleWasmPanic(error, context)
    }
    
    // Handle regular errors
    return this.handleRegularError(error, context)
  }
  
  /**
   * Handle Rust panic with detailed reporting
   */
  private async handleRustPanic(
    error: RustPanic,
    context: ErrorContext
  ): Promise<{ handled: boolean; reportId?: number }> {
    console.error('🦀 Rust Panic Detected:')
    console.error(`   Area: ${error.area}`)
    console.error(`   Message: ${error.message}`)
    
    // Log request details if available
    if (error.request) {
      console.error('   Request Details:')
      console.error(`     ${JSON.stringify(error.request, null, 2)}`)
    }
    
    // Show user-friendly message based on error area
    this.showUserFriendlyPanicMessage(error.area)
    
    // Report panic if enabled
    let reportId: number | undefined
    if (this.reportingEnabled) {
      try {
        reportId = await sendPanic({
          error,
          cliVersion: this.cliVersion,
          enginesVersion: this.enginesVersion,
          getDatabaseVersionSafe: async () => {
            try {
              // Attempt to get database version safely
              return await this.getDatabaseVersionSafe()
            } catch {
              return 'unknown'
            }
          }
        })
        
        console.error(`   Panic reported with ID: ${reportId}`)
      } catch (reportError) {
        console.warn(`   Failed to report panic: ${reportError.message}`)
      }
    }
    
    return { handled: true, reportId }
  }
  
  /**
   * Handle WASM panic
   */
  private handleWasmPanic(
    error: WasmPanic,
    context: ErrorContext
  ): { handled: boolean } {
    console.error('🕸️ WASM Panic Detected:')
    
    const { message, stack } = getWasmError(error)
    console.error(`   Message: ${message}`)
    console.error(`   Stack: ${stack}`)
    
    // Show troubleshooting advice
    console.error('\n💡 Troubleshooting WASM errors:')
    console.error('   • Try updating to the latest Prisma version')
    console.error('   • Check if your schema is valid')
    console.error('   • Restart your development server')
    
    return { handled: true }
  }
  
  /**
   * Handle regular errors
   */
  private handleRegularError(
    error: Error,
    context: ErrorContext
  ): { handled: boolean } {
    console.error('⚠️ Regular Error:')
    console.error(`   Message: ${error.message}`)
    
    if (error.stack) {
      console.error(`   Stack: ${error.stack}`)
    }
    
    // Check for common error patterns
    if (error.message.includes('ECONNREFUSED')) {
      console.error('\n💡 Connection refused - check if your database is running')
    } else if (error.message.includes('authentication failed')) {
      console.error('\n💡 Authentication failed - check your database credentials')
    } else if (error.message.includes('relation') && error.message.includes('does not exist')) {
      console.error('\n💡 Database table missing - run `prisma db push` or migrations')
    }
    
    return { handled: true }
  }
  
  /**
   * Show user-friendly message based on panic area
   */
  private showUserFriendlyPanicMessage(area: ErrorArea): void {
    const messages = {
      [ErrorArea.QUERY_ENGINE_BINARY_CLI]: 'Query engine crashed during database operation',
      [ErrorArea.QUERY_ENGINE_LIBRARY_CLI]: 'Query engine library encountered an internal error',
      [ErrorArea.FMT_CLI]: 'Schema formatter crashed while processing your schema',
      [ErrorArea.INTROSPECTION_CLI]: 'Database introspection failed unexpectedly',
      [ErrorArea.LIFT_CLI]: 'Migration engine encountered an error',
      [ErrorArea.PHOTON_STUDIO]: 'Prisma Studio encountered an internal error'
    }
    
    const message = messages[area] || 'Prisma encountered an unexpected error'
    console.error(`\n📝 ${message}`)
    
    console.error('\n🔧 Next steps:')
    console.error('   • Check your database connection')
    console.error('   • Verify your schema syntax')
    console.error('   • Update to the latest Prisma version')
    console.error('   • Report this issue if it persists')
  }
  
  /**
   * Safely get database version
   */
  private async getDatabaseVersionSafe(): Promise<string> {
    // Implementation would depend on having access to PrismaClient
    // This is a placeholder
    return 'PostgreSQL 15.0'
  }
}

Error Recovery and Retry Logic

import {
  isRustPanic,
  isWasmPanic,
  warnOnce,
  type RustPanic
} from '@prisma/internals'

interface RetryOptions {
  maxAttempts: number
  baseDelay: number
  maxDelay: number
  shouldRetry?: (error: Error, attempt: number) => boolean
}

class ErrorRecoveryManager {
  
  /**
   * Execute operation with retry logic and error recovery
   */
  async withRetry<T>(
    operation: () => Promise<T>,
    options: RetryOptions
  ): Promise<T> {
    const { maxAttempts, baseDelay, maxDelay, shouldRetry } = options
    let lastError: Error | undefined
    
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        return await operation()
      } catch (error) {
        lastError = error as Error
        
        // Don't retry on panics - they indicate serious issues
        if (isRustPanic(error) || isWasmPanic(error)) {
          console.error(`💥 Panic on attempt ${attempt} - not retrying`)
          throw error
        }
        
        // Check custom retry logic
        if (shouldRetry && !shouldRetry(error, attempt)) {
          console.error(`🚫 Custom retry logic rejected attempt ${attempt}`)
          throw error
        }
        
        // Don't retry on last attempt
        if (attempt === maxAttempts) {
          console.error(`🔄 Max attempts (${maxAttempts}) reached`)
          throw error
        }
        
        // Calculate delay with exponential backoff
        const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay)
        
        console.warn(`⏳ Attempt ${attempt} failed, retrying in ${delay}ms...`)
        console.warn(`   Error: ${error.message}`)
        
        await new Promise(resolve => setTimeout(resolve, delay))
      }
    }
    
    throw lastError || new Error('Operation failed after retries')
  }
  
  /**
   * Graceful degradation for non-critical operations
   */
  async withFallback<T>(
    primaryOperation: () => Promise<T>,
    fallbackOperation: () => Promise<T>,
    context: string
  ): Promise<T> {
    try {
      return await primaryOperation()
    } catch (error) {
      if (isRustPanic(error)) {
        warnOnce(`Rust panic in ${context} - using fallback`)
        console.warn(`Primary operation failed due to panic: ${error.message}`)
      } else if (isWasmPanic(error)) {
        warnOnce(`WASM panic in ${context} - using fallback`)
        console.warn(`Primary operation failed due to WASM error`)
      } else {
        console.warn(`Primary operation failed in ${context}: ${error.message}`)
      }
      
      console.log(`🔄 Attempting fallback for ${context}...`)
      return await fallbackOperation()
    }
  }
}

// Usage examples
const recoveryManager = new ErrorRecoveryManager()

// Database operation with retry
const users = await recoveryManager.withRetry(
  async () => {
    return await prisma.user.findMany()
  },
  {
    maxAttempts: 3,
    baseDelay: 1000,
    maxDelay: 5000,
    shouldRetry: (error, attempt) => {
      // Don't retry validation errors
      return !error.message.includes('Invalid')
    }
  }
)

// Schema operation with fallback
const formattedSchema = await recoveryManager.withFallback(
  async () => {
    // Try WASM formatter
    return await formatSchema({ schemas })
  },
  async () => {
    // Fallback to simple string formatting
    warnOnce('Using simple schema formatting as fallback')
    return schemas // Return unformatted
  },
  'schema formatting'
)

Error Monitoring and Analytics

import {
  isRustPanic,
  isWasmPanic,
  getWasmError,
  ErrorArea,
  type RustPanic
} from '@prisma/internals'

interface ErrorMetrics {
  totalErrors: number
  rustPanics: number
  wasmPanics: number
  regularErrors: number
  errorsByArea: Record<ErrorArea, number>
  commonErrors: Record<string, number>
}

class ErrorMonitor {
  private metrics: ErrorMetrics
  private errorHistory: Array<{
    error: Error
    timestamp: Date
    context: string
  }> = []
  
  constructor() {
    this.metrics = {
      totalErrors: 0,
      rustPanics: 0,
      wasmPanics: 0,
      regularErrors: 0,
      errorsByArea: {} as Record<ErrorArea, number>,
      commonErrors: {}
    }
  }
  
  /**
   * Record error for monitoring
   */
  recordError(error: Error, context: string): void {
    this.metrics.totalErrors++
    this.errorHistory.push({
      error,
      timestamp: new Date(),
      context
    })
    
    if (isRustPanic(error)) {
      this.metrics.rustPanics++
      this.recordPanicArea(error.area)
    } else if (isWasmPanic(error)) {
      this.metrics.wasmPanics++
    } else {
      this.metrics.regularErrors++
    }
    
    // Track common error patterns
    const errorKey = this.categorizeError(error)
    this.metrics.commonErrors[errorKey] = (this.metrics.commonErrors[errorKey] || 0) + 1
    
    // Limit history size
    if (this.errorHistory.length > 1000) {
      this.errorHistory = this.errorHistory.slice(-500)
    }
  }
  
  private recordPanicArea(area: ErrorArea): void {
    this.metrics.errorsByArea[area] = (this.metrics.errorsByArea[area] || 0) + 1
  }
  
  private categorizeError(error: Error): string {
    const message = error.message.toLowerCase()
    
    if (message.includes('connection')) return 'connection_error'
    if (message.includes('authentication')) return 'auth_error'
    if (message.includes('timeout')) return 'timeout_error'
    if (message.includes('syntax')) return 'syntax_error'
    if (message.includes('permission')) return 'permission_error'
    if (message.includes('not found')) return 'not_found_error'
    
    return 'other_error'
  }
  
  /**
   * Generate error report
   */
  generateReport(): string {
    const { metrics } = this
    
    let report = '\n📊 Error Monitoring Report\n'
    report += '═'.repeat(40) + '\n\n'
    
    report += `📈 Overall Statistics:\n`
    report += `   Total Errors: ${metrics.totalErrors}\n`
    report += `   Rust Panics: ${metrics.rustPanics}\n`
    report += `   WASM Panics: ${metrics.wasmPanics}\n`
    report += `   Regular Errors: ${metrics.regularErrors}\n\n`
    
    if (metrics.rustPanics > 0) {
      report += `🦀 Panics by Area:\n`
      for (const [area, count] of Object.entries(metrics.errorsByArea)) {
        report += `   ${area}: ${count}\n`
      }
      report += '\n'
    }
    
    if (Object.keys(metrics.commonErrors).length > 0) {
      report += `📋 Common Errors:\n`
      const sortedErrors = Object.entries(metrics.commonErrors)
        .sort(([,a], [,b]) => b - a)
        .slice(0, 5)
      
      for (const [category, count] of sortedErrors) {
        report += `   ${category.replace('_', ' ')}: ${count}\n`
      }
      report += '\n'
    }
    
    // Recent errors
    const recentErrors = this.errorHistory.slice(-5)
    if (recentErrors.length > 0) {
      report += `🕐 Recent Errors:\n`
      for (const { error, timestamp, context } of recentErrors) {
        const timeStr = timestamp.toISOString().substring(11, 19)
        report += `   ${timeStr} [${context}] ${error.message.substring(0, 50)}...\n`
      }
    }
    
    return report
  }
  
  /**
   * Check if error rate is concerning
   */
  getHealthStatus(): {
    status: 'healthy' | 'warning' | 'critical'
    message: string
  } {
    const recentErrors = this.errorHistory.filter(
      entry => Date.now() - entry.timestamp.getTime() < 5 * 60 * 1000 // Last 5 minutes
    )
    
    const panicRate = this.metrics.rustPanics / Math.max(this.metrics.totalErrors, 1)
    
    if (recentErrors.length > 10) {
      return {
        status: 'critical',
        message: `High error rate: ${recentErrors.length} errors in last 5 minutes`
      }
    }
    
    if (panicRate > 0.1) {
      return {
        status: 'critical',
        message: `High panic rate: ${(panicRate * 100).toFixed(1)}% of errors are panics`
      }
    }
    
    if (recentErrors.length > 5 || this.metrics.totalErrors > 20) {
      return {
        status: 'warning',
        message: 'Elevated error levels detected'
      }
    }
    
    return {
      status: 'healthy',
      message: 'Error levels within normal range'
    }
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-prisma--internals

docs

binary-resolution.md

cli-utilities.md

database-operations.md

engine-commands.md

error-handling.md

generators.md

index.md

syntax-highlighting.md

tracing.md

utilities.md

tile.json