The Prisma P2025 error occurs when you try to update, delete, or connect to records that don't exist in your database. This commonly happens with delete operations ("Record to delete does not exist"), update operations ("Record to update not found"), or nested relation connects that reference missing records.
The P2025 error in Prisma indicates that a database operation failed because it requires one or more records that could not be found. This error is thrown as a `PrismaClientKnownRequestError` when operations depend on existing records but those records are missing from the database. The error manifests in several contexts: 1. **Delete operations**: When attempting to delete a record that doesn't exist, you'll see "Record to delete does not exist" 2. **Update operations**: When attempting to update a non-existent record, you'll see "Record to update not found" 3. **Nested connects**: When creating or updating with `connect` to relate records that don't exist, such as "No 'User' record(s) (needed to inline the relation on 'Post' record(s)) was found for a nested connect" 4. **Disconnects on relations**: When trying to disconnect child records in many-to-many relations that cannot be found Note that the same P2025 error code is used for all these scenarios, but the error message text differs to indicate the specific operation type. This error is a logical error rather than a technical one - your database connection and schema are fine, but the data you're trying to work with simply isn't there.
Before performing update or delete operations, verify the record exists:
// Check existence first
const user = await prisma.user.findUnique({
where: { id: userId }
});
if (!user) {
console.log('User not found');
return null; // or throw a custom error
}
// Safe to proceed with operation
const deleted = await prisma.user.delete({
where: { id: userId }
});While this adds an extra query, it prevents P2025 errors and allows you to handle missing records gracefully.
Wrap Prisma operations in try-catch blocks to handle P2025 errors without crashing:
import { Prisma } from '@prisma/client';
async function updateUser(userId: string, data: any) {
try {
const user = await prisma.user.update({
where: { id: userId },
data
});
return user;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2025') {
// Record not found - handle gracefully
console.log('Cannot update: user not found');
return null;
}
}
// Re-throw other errors
throw error;
}
}This approach avoids the extra query while still handling the error case.
If you want an update that never throws when the record doesn't exist, use updateMany:
// updateMany returns a count, not the updated record
const result = await prisma.user.updateMany({
where: { id: userId },
data: { lastLogin: new Date() }
});
if (result.count === 0) {
console.log('No users were updated - user not found');
} else {
console.log(`Updated ${result.count} user(s)`);
}Note: updateMany returns { count: number } rather than the updated record, which may require adjusting your code logic.
When using connect in nested creates or updates, ensure the related records exist:
// Bad: connect to a user that might not exist
const post = await prisma.post.create({
data: {
title: 'My Post',
content: 'Content',
author: {
connect: { id: userId } // P2025 if user doesn't exist
}
}
});
// Good: verify the user exists first
const userExists = await prisma.user.findUnique({
where: { id: userId },
select: { id: true } // minimal select for performance
});
if (!userExists) {
throw new Error('Cannot create post: author not found');
}
const post = await prisma.post.create({
data: {
title: 'My Post',
content: 'Content',
author: { connect: { id: userId } }
}
});For composite keys, ensure all field values in the connect are correct.
If migrating from Prisma 4 to 5+, replace rejectOnNotFound with the new findUniqueOrThrow method:
// Old pattern (deprecated in v4, removed in v5)
const user = await prisma.user.findFirst({
where: { email: '[email protected]' },
rejectOnNotFound: true
});
// New pattern with findFirstOrThrow
try {
const user = await prisma.user.findFirstOrThrow({
where: { email: '[email protected]' }
});
// user is guaranteed to exist here
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2025') {
// Handle not found case
throw new UserNotFoundError();
}
}
throw error;
}findUniqueOrThrow and findFirstOrThrow throw P2025 when no record is found, replacing the deprecated rejectOnNotFound behavior.
When you're not sure if a related record exists, use connectOrCreate instead of connect:
// Instead of connect (which throws P2025 if category doesn't exist)
const post = await prisma.post.create({
data: {
title: 'My Post',
category: { connect: { name: 'Technology' } }
}
});
// Use connectOrCreate to handle both cases
const post = await prisma.post.create({
data: {
title: 'My Post',
category: {
connectOrCreate: {
where: { name: 'Technology' },
create: {
name: 'Technology',
description: 'Tech articles'
}
}
}
}
});connectOrCreate will connect to the existing record if found, or create it if it doesn't exist, completely avoiding P2025 errors.
Performance Trade-offs: Checking if a record exists before operating on it requires an extra database query. In high-throughput applications, consider:
- Using try-catch error handling instead of pre-checks (optimistic approach)
- Leveraging updateMany which doesn't throw on missing records
- Batching existence checks when operating on multiple records
- Using database-level constraints and foreign keys to maintain data integrity
Race Conditions in Concurrent Environments: Even checking for existence doesn't guarantee the record will still exist when you try to update/delete it. In multi-user applications:
- Use transactions to wrap check-and-operate sequences
- Implement optimistic locking with version numbers
- Accept that some P2025 errors are unavoidable and handle them gracefully
- Consider database advisory locks for critical operations
Error Code P2025 vs NotFoundError: In Prisma v6, the NotFoundError class was removed in favor of PrismaClientKnownRequestError with code P2025. When using findUniqueOrThrow() or findFirstOrThrow(), always check for error.code === 'P2025' to detect not-found scenarios.
Nested Relation Debugging: When P2025 occurs with nested connects, the error message specifies which model and field caused the issue. Example: "No 'User' record(s) (needed to inline the relation on 'Post' record(s)) was found for a nested connect on one-to-many relation". This tells you:
- The missing record type (User)
- The dependent record type (Post)
- The relation type (one-to-many)
Use this information to identify which ID in your connect operation is incorrect.
Testing Strategies: To prevent P2025 errors in production:
- Write integration tests that simulate missing record scenarios
- Test delete operations twice to verify idempotency handling
- Use database seeding to ensure required reference data exists
- Test cross-environment scenarios (dev vs staging vs prod data differences)
- Implement monitoring to track P2025 frequency and patterns
Migration from rejectOnNotFound: If upgrading from Prisma 4 to 5+, note that rejectOnNotFound was deprecated and removed. Search your codebase for rejectOnNotFound and replace with findUniqueOrThrow or findFirstOrThrow, ensuring you have proper P2025 error handling in place.
P6005: Invalid parameters (Pulse)
How to fix "P6005: Invalid parameters (Pulse)" in Prisma
P2011: Null constraint violation on the field
How to fix "P2011: Null constraint violation" in Prisma
P2009: Failed to validate the query: {validation_error}
How to fix "P2009: Failed to validate the query" in Prisma
P2007: Data validation error
How to fix "P2007: Data validation error" in Prisma
P1013: The provided database string is invalid
The provided database string is invalid