Skip to content
Back to skills

Handling Transaction Errors

ASecurity

Handle transaction errors properly with P-code checking and timeout configuration. Use when implementing transaction error recovery.

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 9, 2026
businesstypescriptdatabase

Works with

  • cli

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill handling-transaction-errors --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Handling Transaction Errors?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Handling Transaction Errors
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-handling-transaction-errors/badge)](https://www.skillsdirectory.com/skills/majiayu000-handling-transaction-errors)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: handling-transaction-errors
description: Handle transaction errors properly with P-code checking and timeout configuration. Use when implementing transaction error recovery.
allowed-tools: Read, Write, Edit
---

# 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.

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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:
- `maxWait`: Maximum time (ms) to wait for transaction to start (default: 2000)
- `timeout`: Maximum time (ms) for transaction to complete (default: 5000)

### Handling Timeout Errors

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
await prisma.$transaction(async (tx) => {
  for (let i = 0; i < 10000; i++) {
    await tx.log.create({ data: { message: `Log ${i}` } });
  }
});
```

### DO: Configure Appropriate Timeouts

```typescript
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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.