Security: Error Exposure Prevention
This skill teaches Claude how to handle Prisma errors securely by transforming detailed database errors into user-friendly messages while preserving debugging information in logs.
<role> This skill prevents leaking sensitive database information (P-codes, table names, column details, constraints) to API clients while maintaining comprehensive server-side logging for debugging. </role> <when-to-activate> This skill activates when: - Implementing API error handlers or middleware - Working with try/catch blocks around Prisma operations - Building user-facing error responses - Setting up logging infrastructure - User mentions error handling, error messages, or API responses </when-to-activate> <overview> Prisma errors contain detailed database information including: - P-codes (P2002, P2025, etc.) revealing database operations - Table and column names exposing schema structure - Constraint names showing relationships - Query details revealing business logic
Security Risk: Exposing this information helps attackers:
- •Map database schema
- •Identify validation rules
- •Craft targeted attacks
- •Discover business logic
Solution Pattern: Transform errors for clients, log full details server-side.
Key capabilities:
- •P-code to user message transformation
- •Error sanitization removing sensitive details
- •Server-side logging with full context
- •Production-ready error middleware </overview>
Phase 1: Error Detection
- •Wrap Prisma operations in try/catch
- •Identify error type (Prisma vs generic)
- •Extract P-code if present
Phase 2: Error Transformation
- •Map P-code to user-friendly message
- •Remove database-specific details
- •Generate safe generic message for unknown errors
- •Preserve error context for logging
Phase 3: Response and Logging
- •Log full error details server-side (P-code, stack, query)
- •Return sanitized message to client
- •Include generic error ID for support correlation </workflow>
Development Environment:
- •Log full error details including stack traces
- •Optionally include P-codes in API response for debugging
- •Show detailed validation errors
- •Enable query logging
Production Environment:
- •NEVER expose P-codes to clients
- •Log errors with correlation IDs
- •Return generic user messages
- •Monitor error rates for P2024 (connection timeout)
- •Alert on P2002 spikes (potential brute force)
Framework-Specific Patterns
Next.js App Router:
export async function POST(request: Request) {
try {
const data = await request.json()
const result = await prisma.user.create({ data })
return Response.json(result)
} catch (error) {
return handlePrismaError(error)
}
}
Express/Fastify:
app.use((err, req, res, next) => {
if (isPrismaError(err)) {
const { status, message, errorId } = transformPrismaError(err)
logger.error({ err, errorId, userId: req.user?.id })
return res.status(status).json({ error: message, errorId })
}
next(err)
})
Pattern: P-code to User Message
import { Prisma } from '@prisma/client'
function transformPrismaError(error: unknown) {
const errorId = crypto.randomUUID()
if (error instanceof Prisma.PrismaClientKnownRequestError) {
switch (error.code) {
case 'P2002':
return {
status: 409,
message: 'A record with this information already exists.',
errorId,
logDetails: {
code: error.code,
meta: error.meta,
target: error.meta?.target
}
}
case 'P2025':
return {
status: 404,
message: 'The requested resource was not found.',
errorId,
logDetails: {
code: error.code,
meta: error.meta
}
}
case 'P2003':
return {
status: 400,
message: 'The provided reference is invalid.',
errorId,
logDetails: {
code: error.code,
meta: error.meta,
field: error.meta?.field_name
}
}
case 'P2014':
return {
status: 400,
message: 'The change violates a required relationship.',
errorId,
logDetails: {
code: error.code,
meta: error.meta
}
}
default:
return {
status: 500,
message: 'An error occurred while processing your request.',
errorId,
logDetails: {
code: error.code,
meta: error.meta
}
}
}
}
if (error instanceof Prisma.PrismaClientValidationError) {
return {
status: 400,
message: 'The provided data is invalid.',
errorId,
logDetails: {
type: 'ValidationError',
message: error.message
}
}
}
return {
status: 500,
message: 'An unexpected error occurred.',
errorId,
logDetails: {
type: error?.constructor?.name,
message: error instanceof Error ? error.message : 'Unknown error'
}
}
}
Example 2: Production Error Handler
Pattern: Middleware with Logging
import { Prisma } from '@prisma/client'
import { logger } from './logger'
export function handlePrismaError(error: unknown, context?: Record<string, unknown>) {
const { status, message, errorId, logDetails } = transformPrismaError(error)
logger.error({
errorId,
...logDetails,
context,
stack: error instanceof Error ? error.stack : undefined,
timestamp: new Date().toISOString()
})
return {
status,
body: {
error: message,
errorId
}
}
}
export async function createUser(data: { email: string; name: string }) {
try {
return await prisma.user.create({ data })
} catch (error) {
const { status, body } = handlePrismaError(error, {
operation: 'createUser',
email: data.email
})
throw new ApiError(status, body)
}
}
Example 3: Environment-Aware Error Handling
Pattern: Development vs Production
const isDevelopment = process.env.NODE_ENV === 'development'
function formatErrorResponse(error: unknown, errorId: string) {
const { status, message, logDetails } = transformPrismaError(error)
const baseResponse = {
error: message,
errorId
}
if (isDevelopment && error instanceof Prisma.PrismaClientKnownRequestError) {
return {
...baseResponse,
debug: {
code: error.code,
meta: error.meta,
clientVersion: Prisma.prismaVersion.client
}
}
}
return baseResponse
}
Example 4: Specific Field Error Extraction
Pattern: P2002 Constraint Details
function extractP2002Details(error: Prisma.PrismaClientKnownRequestError) {
if (error.code !== 'P2002') return null
const target = error.meta?.target as string[] | undefined
if (!target || target.length === 0) {
return 'A record with this information already exists.'
}
const fieldMap: Record<string, string> = {
email: 'email address',
username: 'username',
phone: 'phone number',
slug: 'identifier'
}
const fieldName = target[0]
const friendlyName = fieldMap[fieldName] || 'information'
return `A record with this ${friendlyName} already exists.`
}
function transformPrismaError(error: unknown) {
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
const message = extractP2002Details(error)
return {
status: 409,
message,
errorId: crypto.randomUUID(),
logDetails: { code: 'P2002', target: error.meta?.target }
}
}
}
Client Response (JSON):
{
"error": "User-friendly message without database details",
"errorId": "uuid-for-correlation"
}
Server Log (Structured):
{
"level": "error",
"errorId": "uuid-for-correlation",
"code": "P2002",
"meta": { "target": ["email"] },
"context": { "operation": "createUser", "userId": "123" },
"stack": "Error stack trace...",
"timestamp": "2025-11-21T10:30:00Z"
}
Development Response (Optional Debug):
{
"error": "User-friendly message",
"errorId": "uuid-for-correlation",
"debug": {
"code": "P2002",
"meta": { "target": ["email"] },
"clientVersion": "6.0.0"
}
}
MUST:
- •Transform ALL Prisma errors before sending to clients
- •Log full error details server-side with correlation IDs
- •Remove P-codes from production API responses
- •Remove table/column names from client messages
- •Remove constraint names from client messages
- •Use generic messages for unexpected errors
SHOULD:
- •Include error IDs for support correlation
- •Monitor error rates for security patterns
- •Use structured logging for error analysis
- •Implement field-specific messages for P2002
- •Differentiate 404 (P2025) from 400/500 errors
NEVER:
- •Expose P-codes to clients in production
- •Include error.meta in API responses
- •Show Prisma stack traces to clients
- •Reveal table or column names
- •Display constraint names
- •Return raw error.message to clients
Common P-codes to Handle
P2002 - Unique constraint violation
- •Status: 409 Conflict
- •Message: "A record with this information already exists"
P2025 - Record not found
- •Status: 404 Not Found
- •Message: "The requested resource was not found"
P2003 - Foreign key constraint violation
- •Status: 400 Bad Request
- •Message: "The provided reference is invalid"
P2014 - Required relation violation
- •Status: 400 Bad Request
- •Message: "The change violates a required relationship"
P2024 - Connection timeout
- •Status: 503 Service Unavailable
- •Message: "Service temporarily unavailable"
- •Action: Log urgently, indicates connection pool exhaustion </constraints>
After implementing error handling:
- •
Verify No P-codes Exposed:
- •Search API responses for "P20" pattern
- •Test each error scenario
- •Check production logs vs API responses
- •
Confirm Logging Works:
- •Trigger known errors (P2002, P2025)
- •Verify errorId appears in both logs and response
- •Confirm full error details in logs only
- •
Test Error Scenarios:
- •Unique constraint violation (create duplicate)
- •Not found (query non-existent record)
- •Foreign key violation (invalid reference)
- •Validation error (missing required field)
- •
Review Environment Behavior:
- •Production: No P-codes, no meta, no stack
- •Development: Optional debug info
- •Logs: Full details in both environments </validation>
Integration with SECURITY-input-validation
Error exposure prevention works with input validation:
- •
Input Validation (SECURITY-input-validation skill):
- •Validate data before Prisma operations
- •Return validation errors with field-level messages
- •Prevent malformed data reaching database
- •
Error Transformation (this skill):
- •Handle database-level errors
- •Transform Prisma errors to user messages
- •Log server-side for debugging
Pattern:
async function createUser(input: unknown) {
const validation = userSchema.safeParse(input)
if (!validation.success) {
return {
status: 400,
body: {
error: 'Invalid user data',
fields: validation.error.flatten().fieldErrors
}
}
}
try {
return await prisma.user.create({ data: validation.data })
} catch (error) {
const { status, body } = handlePrismaError(error)
return { status, body }
}
}
Validation catches format issues, error transformation handles database constraints.
Related Skills
Error Handling and Validation:
- •If sanitizing error messages for user display, use the sanitizing-user-inputs skill from typescript for safe error formatting
- •If customizing Zod validation errors, use the customizing-errors skill from zod-4 for user-friendly error messages