Claude-skill-registry handling-transaction-errors
Handle transaction errors properly with P-code checking and timeout configuration. Use when implementing transaction error recovery.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/handling-transaction-errors" ~/.claude/skills/majiayu000-claude-skill-registry-handling-transaction-errors && rm -rf "$T"
manifest:
skills/data/handling-transaction-errors/SKILL.mdsource content
Transaction Error Handling
Handle transaction errors properly with P-code checking, timeout configuration, and recovery patterns.
Error Catching in Transactions
All transaction operations must be wrapped in try/catch blocks to handle failures gracefully.
try { await prisma.$transaction(async (tx) => { const user = await tx.user.create({ data: { email: 'user@example.com' } }); await tx.profile.create({ data: { userId: user.id, bio: 'Hello' } }); }); } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { console.error(`Transaction failed: ${error.code}`); } throw error; }
P-Code Error Handling
P2002: Unique Constraint Violation
import { Prisma } from '@prisma/client'; try { await prisma.$transaction(async (tx) => { await tx.user.create({ data: { email: 'duplicate@example.com' } }); }); } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002' ) { const target = error.meta?.target as string[]; throw new Error(`Unique constraint failed on: ${target.join(', ')}`); } throw error; }
P2025: Record Not Found
try { await prisma.$transaction(async (tx) => { const user = await tx.user.update({ where: { id: nonExistentId }, data: { name: 'New Name' } }); }); } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025' ) { throw new Error('Record to update not found'); } throw error; }
Comprehensive P-Code Handler
function handlePrismaError(error: unknown): Error { if (error instanceof Prisma.PrismaClientKnownRequestError) { switch (error.code) { case 'P2002': return new Error( `Unique constraint violation: ${error.meta?.target}` ); case 'P2025': return new Error('Record not found'); case 'P2034': return new Error('Transaction conflict, please retry'); default: return new Error(`Database error: ${error.code}`); } } if (error instanceof Prisma.PrismaClientUnknownRequestError) { return new Error('Unknown database error'); } if (error instanceof Prisma.PrismaClientValidationError) { return new Error('Invalid query parameters'); } return error instanceof Error ? error : new Error('Unknown error'); } try { await prisma.$transaction(async (tx) => { await tx.user.create({ data: { email: 'test@example.com' } }); }); } catch (error) { throw handlePrismaError(error); }
Timeout Configuration
Basic Timeout Settings
await prisma.$transaction( async (tx) => { await tx.user.create({ data: { email: 'user@example.com' } }); await tx.profile.create({ data: { userId: 1, bio: 'Bio' } }); }, { maxWait: 5000, timeout: 10000, } );
Configuration:
: Maximum time (ms) to wait for transaction to start (default: 2000)maxWait
: Maximum time (ms) for transaction to complete (default: 5000)timeout
Handling Timeout Errors
try { await prisma.$transaction( async (tx) => { await tx.user.findMany(); await new Promise(resolve => setTimeout(resolve, 15000)); }, { timeout: 10000 } ); } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { if (error.message.includes('timeout')) { throw new Error('Transaction timed out, please try again'); } } throw error; }
Long-Running Transactions
await prisma.$transaction( async (tx) => { const users = await tx.user.findMany(); for (const user of users) { await tx.auditLog.create({ data: { userId: user.id, action: 'BATCH_UPDATE', timestamp: new Date(), } }); } }, { maxWait: 10000, timeout: 60000, } );
Recovery Patterns
Retry Strategy with Exponential Backoff
async function transactionWithRetry<T>( operation: (tx: Prisma.TransactionClient) => Promise<T>, maxRetries = 3 ): Promise<T> { let lastError: Error | null = null; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await prisma.$transaction(operation, { timeout: 10000, }); } catch (error) { lastError = error instanceof Error ? error : new Error('Unknown error'); if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2034' ) { const delay = Math.pow(2, attempt) * 100; await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw error; } } throw new Error(`Transaction failed after ${maxRetries} retries: ${lastError?.message}`); } const result = await transactionWithRetry(async (tx) => { return await tx.user.create({ data: { email: 'user@example.com' } }); });
Idempotent Retry Pattern
async function upsertWithRetry(email: string, name: string) { try { return await prisma.$transaction(async (tx) => { return await tx.user.upsert({ where: { email }, create: { email, name }, update: { name }, }); }); } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002' ) { return await prisma.user.update({ where: { email }, data: { name }, }); } throw error; } }
Graceful Degradation
async function transferFunds(fromId: number, toId: number, amount: number) { try { return await prisma.$transaction( async (tx) => { const from = await tx.account.update({ where: { id: fromId }, data: { balance: { decrement: amount } }, }); if (from.balance < 0) { throw new Error('Insufficient funds'); } await tx.account.update({ where: { id: toId }, data: { balance: { increment: amount } }, }); return { success: true }; }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable, timeout: 5000, } ); } catch (error) { if (error instanceof Error && error.message === 'Insufficient funds') { return { success: false, reason: 'insufficient_funds' }; } if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025' ) { return { success: false, reason: 'account_not_found' }; } throw error; } }
Compensating Transactions
async function createOrderWithInventory( productId: number, quantity: number, userId: number ) { let orderId: number | null = null; try { const result = await prisma.$transaction(async (tx) => { const product = await tx.product.update({ where: { id: productId }, data: { stock: { decrement: quantity } }, }); if (product.stock < 0) { throw new Error('Insufficient stock'); } const order = await tx.order.create({ data: { userId, productId, quantity, status: 'PENDING', }, }); orderId = order.id; return order; }); return result; } catch (error) { if (orderId) { await prisma.order.update({ where: { id: orderId }, data: { status: 'FAILED' }, }); } throw error; } }
Isolation Level Error Handling
try { await prisma.$transaction( async (tx) => { const balance = await tx.account.findUnique({ where: { id: accountId }, }); await tx.account.update({ where: { id: accountId }, data: { balance: balance!.balance + amount }, }); }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable, } ); } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2034' ) { throw new Error('Serialization failure, transaction will be retried'); } throw error; }
Common Patterns
Validation Before Transaction
async function createUserWithProfile(email: string, name: string) { const existing = await prisma.user.findUnique({ where: { email }, }); if (existing) { throw new Error('User already exists'); } try { return await prisma.$transaction(async (tx) => { const user = await tx.user.create({ data: { email, name }, }); await tx.profile.create({ data: { userId: user.id }, }); return user; }); } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002' ) { throw new Error('User was created by another request'); } throw error; } }
Nested Error Context
class TransactionError extends Error { constructor( message: string, public readonly code: string, public readonly context?: Record<string, unknown> ) { super(message); this.name = 'TransactionError'; } } async function complexTransaction(data: unknown) { try { return await prisma.$transaction(async (tx) => { const user = await tx.user.create({ data: data as Prisma.UserCreateInput, }); return user; }); } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new TransactionError( 'Transaction failed', error.code, { meta: error.meta, data } ); } throw error; } }
Anti-Patterns
DON'T: Ignore Error Types
try { await prisma.$transaction(async (tx) => { await tx.user.create({ data: { email: 'test@example.com' } }); }); } catch (error) { console.error('Error occurred'); }
DO: Handle Specific Error Types
try { await prisma.$transaction(async (tx) => { await tx.user.create({ data: { email: 'test@example.com' } }); }); } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { console.error(`Database error ${error.code}: ${error.message}`); } else { console.error('Unexpected error:', error); } throw error; }
DON'T: Use Default Timeouts for Long Operations
await prisma.$transaction(async (tx) => { for (let i = 0; i < 10000; i++) { await tx.log.create({ data: { message: `Log ${i}` } }); } });
DO: Configure Appropriate Timeouts
await prisma.$transaction( async (tx) => { const logs = Array.from({ length: 10000 }, (_, i) => ({ message: `Log ${i}`, })); await tx.log.createMany({ data: logs }); }, { timeout: 30000 } );
Related Skills
TypeScript Error Handling:
- If implementing runtime checks for error codes, use the using-runtime-checks skill from typescript for assertion and guard patterns