Prisma Errors — Handling Database Errors and Edge Cases
In this tutorial, you will learn about Prisma Errors. We cover key concepts, practical examples, and best practices to help you master this topic.
Prisma error handling involves understanding error types (known, unknown, connection), handling specific error codes like P2002 (unique constraint) and P2025 (record not found), and implementing retries.
What You'll Learn
By the end of this lesson you will distinguish PrismaClientKnownRequestError, PrismaClientUnknownRequestError, and PrismaClientValidationError, handle common error codes, and implement retry logic.
Why It Matters
Database errors are inevitable in production. Unique constraint violations, connection failures, and record-not-found errors must be handled gracefully to provide good user experience and maintain data integrity.
Real-World Use
DodaZIP's API handles Prisma errors at the controller level. A P2025 error returns 404, P2002 returns 409 with the conflicting field, and connection errors trigger a retry with exponential backoff.
Error Types
Prisma throws three categories of errors.
import {
PrismaClientKnownRequestError,
PrismaClientUnknownRequestError,
PrismaClientValidationError,
PrismaClientInitializationError,
PrismaClientRustPanicError,
} from '@prisma/client/runtime/library';
// KnownRequestError: Database returns a known error
// UnknownRequestError: Database returns an unknown error
// ValidationError: Invalid input to Prisma Client
// InitializationError: Prisma Client failed to start
// RustPanicError: Internal Prisma Engine error
# error_types.py
# Prisma error categories
def error_categories():
print("Prisma Error Categories:")
print()
errors = {
"PrismaClientKnownRequestError": "Database returned a known error code (P2002, P2025, etc.)",
"PrismaClientUnknownRequestError": "Database returned an unrecognized error",
"PrismaClientValidationError": "Invalid input (wrong types, missing required fields)",
"PrismaClientInitializationError": "Prisma Client could not connect to the database",
"PrismaClientRustPanicError": "Internal Prisma Engine error (rare, contact support)",
}
for name, desc in errors.items():
print(f" {name:40s} {desc}")
error_categories()
Common Error Codes
Handle the most frequent Prisma error codes.
// Common error codes and their handling
try {
const user = await prisma.user.create({
data: { email: 'existing@test.com' },
});
} catch (error) {
if (error instanceof PrismaClientKnownRequestError) {
switch (error.code) {
case 'P2002': {
// Unique constraint violation
const field = error.meta?.target as string[];
console.log(`Unique constraint on ${field.join(', ')}`);
throw new ConflictError('Email already exists');
}
case 'P2025': {
// Record not found
console.log('Record not found:', error.meta?.cause);
throw new NotFoundError('User not found');
}
case 'P2003': {
// Foreign key constraint violation
console.log('Foreign key violation:', error.meta?.field_name);
throw new BadRequestError('Referenced record does not exist');
}
case 'P2014': {
// Required relation violation
throw new BadRequestError('Required relation not provided');
}
default:
console.error('Unhandled Prisma error:', error.code, error.message);
throw error;
}
} else if (error instanceof PrismaClientValidationError) {
throw new BadRequestError('Invalid data provided');
}
}
# error_codes.py
# Common Prisma error codes
def common_error_codes():
codes = {
"P2000": "Value too long for column",
"P2002": "Unique constraint violation",
"P2003": "Foreign key constraint violation",
"P2004": "Database constraint violation",
"P2005": "Invalid value for field type",
"P2014": "Required relation violation",
"P2015": "Related record not found",
"P2016": "Query interpretation error",
"P2025": "Record not found (update/delete)",
"P2024": "Connection pool timeout",
"P1000": "Authentication failed",
"P1001": "Cannot connect to database host",
}
print("Common Prisma Error Codes:")
print(f" {'Code':8s} | {'Description'}")
print(" " + "-" * 45)
for code, desc in codes.items():
print(f" {code:8s} | {desc}")
common_error_codes()
Global Error Handler
Create a centralized error handling middleware.
// error-handler.ts
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
import { Request, Response, NextFunction } from 'express';
function prismaErrorHandler(
error: Error,
req: Request,
res: Response,
next: NextFunction
) {
if (error instanceof PrismaClientKnownRequestError) {
const errorMap: Record<string, [number, string]> = {
P2002: [409, 'Resource already exists'],
P2025: [404, 'Resource not found'],
P2003: [400, 'Related resource not found'],
P2014: [400, 'Invalid relation'],
P2024: [503, 'Database connection timeout'],
};
const [status, message] = errorMap[error.code] || [500, 'Database error'];
res.status(status).json({
error: message,
code: error.code,
...(error.meta && { details: error.meta }),
});
return;
}
if (error instanceof PrismaClientValidationError) {
res.status(400).json({ error: 'Invalid data' });
return;
}
next(error);
}
# global_handler.py
# Error handler structure
def error_handler():
print("Global Error Handler Pattern:")
print()
print("Error code → HTTP status mapping:")
print(" P2002 (Unique) → 409 Conflict")
print(" P2025 (Not found) → 404 Not Found")
print(" P2003 (FK) → 400 Bad Request")
print(" P2024 (Timeout) → 503 Service Unavailable")
print(" Other → 500 Internal Server Error")
print()
print("Response format:")
print(" {")
print(' "error": "Descriptive message",')
print(' "code": "P2002",')
print(' "details": { ... }')
print(" }")
error_handler()
Retry Logic
Implement retry for transient errors.
// Retry helper for transient database errors
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 100
): Promise<T> {
let lastError: Error;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
// Only retry on connection/timeout errors
if (error instanceof PrismaClientKnownRequestError) {
if (error.code !== 'P2024' && error.code !== 'P1001') {
throw error; // Don't retry non-transient errors
}
} else if (!(error instanceof PrismaClientInitializationError)) {
throw error; // Don't retry other error types
}
// Exponential backoff with jitter
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 100;
console.log(`Retrying after ${delay}ms (attempt ${attempt + 1})`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError!;
}
// Usage
const user = await withRetry(() =>
prisma.user.findUnique({ where: { id: 1 } })
);
# retry.py
# Retry logic patterns
def retry_patterns():
print("Retry Logic Patterns:")
print()
print("Retryable errors:")
print(" - P1001: Cannot connect to database")
print(" - P2024: Connection pool timeout")
print(" - PrismaClientInitializationError")
print()
print("Non-retryable errors:")
print(" - P2002: Unique violation")
print(" - P2025: Record not found")
print(" - P2003: Foreign key violation")
print(" - PrismaClientValidationError")
print()
print("Backoff strategies:")
print(" - Fixed delay: always wait N ms")
print(" - Exponential: delay * 2^attempt")
print(" - Jitter: add random factor to avoid thundering herd")
retry_patterns()
Common Mistakes
Catching Prisma errors too broadly: Catching generic Error instead of specific Prisma types loses error code information and context.
Not using error.code for handling: The error code (P2002, P2025) is the most reliable way to handle Prisma errors. Don't parse the message string.
Retrying non-retryable errors: Unique constraint violations and missing records will never succeed on retry. Only retry connection and timeout errors.
Exposing internal error details: Prisma errors may contain database schema details. Strip sensitive information before sending to clients.
Not disconnecting on PrismaClientInitializationError: If the client fails to initialize, the application cannot function. Exit gracefully or restart.
Practice Questions
What are the main Prisma error types? PrismaClientKnownRequestError, UnknownRequestError, ValidationError, InitializationError, RustPanicError.
What does error code P2002 mean? Unique constraint violation -- a record with the same unique field value already exists.
What does error code P2025 mean? Record not found -- an update or delete operation's where condition matched no records.
Which errors should you retry? Connection errors (P1001, P2024, InitializationError). Most other errors are non-transient.
Challenge: Create a comprehensive error handling middleware that maps Prisma error codes to HTTP status codes, returns user-friendly messages, logs the error, and handles edge cases.
FAQ
Mini Project
Create an error handling module for a production Prisma application: error class hierarchy, HTTP status mapping, retry utility, and global error handler for Express.
def error_module():
print("Error Handling Module:")
print()
print("Classes:")
print(" DatabaseError (base)")
print(" - UniqueViolationError (P2002) → 409")
print(" - NotFoundError (P2025) → 404")
print(" - ForeignKeyError (P2003) → 400")
print(" - ConnectionError (P1001, P2024) → 503")
print()
print("Utilities:")
print(" withRetry(fn, maxRetries=3)")
print(" handlePrismaError(error) → HTTP response")
print(" isRetryableError(error) → boolean")
error_module()
What's Next
Next: Prisma with Express for API integration.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro