The Prisma P2010 error occurs when using $queryRaw or $executeRaw with invalid SQL syntax, type mismatches, or database constraint violations. This error surfaces underlying database problems through Prisma's raw query interface.
The P2010 error is a Prisma error that wraps database-level errors when executing raw SQL queries using `$queryRaw` or `$executeRaw`. The error message follows the format: "Raw query failed. Code: {code}. Message: {message}" where `code` and `message` come from the underlying database driver. Unlike Prisma's higher-level query API, raw queries bypass Prisma's schema validation and query building. This means: - Database syntax errors reach you directly - Type mismatches between parameters and SQL types cause failures - Parameter serialization issues are exposed - SQL injection protection is your responsibility (use template literals) The P2010 error can indicate anything from SQL syntax errors to incompatible parameter types, making diagnosis require examining the underlying database error code.
The P2010 error includes the underlying database error code and message:
PrismaClientKnownRequestError: Raw query failed.
Code: `42601`.
Message: `db error: ERROR: syntax error at or near "$1"`If you see "Code: N/A. Message: N/A", this is a known bug with non-returning queries or very large queries. The query may still have executed successfully - check your database.
Common error codes:
- 42601: Syntax error (PostgreSQL)
- 42P01: Table/relation doesn't exist (PostgreSQL)
- 08P01: Parameter serialization error (PostgreSQL)
- 1064: MySQL syntax error
- HY000: SQL Server general error
Before debugging Prisma, test the query directly in your database client (psql, mysql, DBeaver, etc.):
# PostgreSQL
psql "postgresql://user:pass@localhost/dbname"
\# SELECT * FROM users WHERE id = 123;
# MySQL
mysql -u user -p database
> SELECT * FROM users WHERE id = 123;Run the exact same query to see if the database reports an error. This isolates whether the issue is:
- Database-level (syntax, constraints)
- Prisma-level (parameter serialization)
Check for common syntax mistakes:
// WRONG: Missing semicolon won't cause P2010, but typos will
await prisma.$queryRaw`SELECT * FROM user`; // "user" might not exist
// WRONG: Spaces in column names need quotes
await prisma.$queryRaw`SELECT my column FROM users`;
// CORRECT: Properly quoted identifiers
await prisma.$queryRaw`SELECT "myColumn" FROM "Users"`;
// CORRECT: In PostgreSQL, use quotes if case-sensitive names
await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;PostgreSQL is case-sensitive for quoted names:
// These are different in PostgreSQL:
`SELECT * FROM users` // Becomes lowercase: users
`SELECT * FROM "Users"` // Exact case: Users
// If your table is created as "Users", you must quote it
await prisma.$queryRaw`SELECT * FROM "Users"`;Ensure your parameters match the database column types:
// WRONG: Passing string to an integer column
const userId = "123"; // String from URL param
await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
// Error: Type mismatch or parameter serialization failure
// CORRECT: Cast to the right type
const userId = parseInt("123", 10);
await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
// CORRECT: Use explicit SQL type casting
const userId = "123";
await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}::integer`;Special cases:
PostgreSQL INTERVAL type:
// WRONG: INTERVAL expects specific format
await prisma.$queryRaw`SELECT * FROM events WHERE duration > ${'1 hour'}`;
// CORRECT: Cast to INTERVAL
await prisma.$queryRaw`SELECT * FROM events WHERE duration > ${'1 hour'}::interval`;BigInt parameters:
// WRONG: BigInt may not serialize correctly
const largeId = BigInt("9007199254740991");
await prisma.$queryRaw`SELECT * FROM items WHERE id = ${largeId}`;
// CORRECT: Convert to string for large numbers
const largeId = "9007199254740991";
await prisma.$queryRaw`SELECT * FROM items WHERE id = ${largeId}::bigint`;Never use string interpolation for dynamic SQL. Use the template literal syntax or Prisma.sql helper:
// WRONG: SQL injection risk + parameter issues
const tableName = "users";
const query = `SELECT * FROM ${tableName}`;
await prisma.$queryRaw(query);
// CORRECT: Use tagged template literal (Prisma's parameter binding)
await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
// CORRECT: For dynamic SQL fragments, use Prisma.sql
import { Prisma } from '@prisma/client';
const whereFragment = Prisma.sql`WHERE email = ${email}`;
await prisma.$queryRaw`SELECT * FROM users ${whereFragment}`;
// CORRECT: Use Prisma.sql for building complex queries safely
const query = Prisma.sql`
SELECT * FROM users
WHERE age > ${minAge}
AND status = ${status}
`;
const results = await prisma.$queryRaw(query);Important: Never use string interpolation for identifiers (table/column names). If you need dynamic identifiers, use $queryRawUnsafe with extreme caution:
// UNSAFE: Only use if you fully control the input
const tableName = "users"; // Hardcoded or heavily validated
await prisma.$queryRawUnsafe(`SELECT * FROM ${tableName}`);If you're executing very large raw queries (inserting 20k+ rows or similar), ensure you're using Prisma 4.4.0 or later:
npm list @prisma/clientIf your version is older, upgrade:
npm install @prisma/client@latest
npx prisma generateLarge query issues with "Code: N/A. Message: N/A" were fixed in 4.4.0. After upgrading, re-run your large queries.
When using $executeRaw with queries that don't return result sets (INSERT, UPDATE, DELETE), Prisma may return "Code: N/A. Message: N/A" even though the query succeeded:
// This might throw P2010 with N/A codes but still execute
try {
const result = await prisma.$executeRaw`
INSERT INTO logs (message, level)
VALUES (${message}, ${level})
`;
console.log('Rows affected:', result); // Still works despite error
} catch (error) {
// Check if it's an N/A error
if (error.code === 'P2010' && error.message.includes('N/A')) {
console.log('Query likely succeeded despite N/A error');
// Verify by querying the data
const inserted = await prisma.$queryRaw`SELECT * FROM logs WHERE message = ${message}`;
if (inserted.length > 0) {
console.log('Confirmed: data was inserted');
}
} else {
throw error;
}
}In newer Prisma versions, this is fixed. Consider upgrading if you encounter this pattern.
## Raw Query Best Practices
### Using Prisma.sql Helper
The Prisma.sql helper makes raw query composition type-safe and prevents SQL injection:
import { Prisma } from '@prisma/client';
// Combine multiple SQL fragments
const orderBy = Prisma.sql`ORDER BY created_at DESC`;
const limit = Prisma.sql`LIMIT ${10}`;
const query = Prisma.sql`
SELECT * FROM posts
WHERE published = true
${orderBy}
${limit}
`;
const posts = await prisma.$queryRaw(query);### Database-Specific Considerations
PostgreSQL:
- Use double quotes for case-sensitive identifiers: "UserId"
- Cast types explicitly when ambiguous: ${value}::integer
- INTERVAL types need proper syntax: '1 hour'::interval
- Use ::bigint for large integers instead of BigInt
MySQL:
- Backticks for reserved keywords: \table\`
- Type casting uses CAST(value AS type): CAST(${value} AS SIGNED)
- JSON operations require proper syntax: JSON_EXTRACT(${json}, '$.field')`
SQLite:
- Very forgiving with types
- Limited type casting syntax
- No INTERVAL type - use integer durations
### Performance Considerations
1. Use raw queries only when necessary: Prisma's query builder is optimized and handles parameterization correctly
2. Add indexes for raw query conditions: Indices aren't automatically applied to raw queries
3. Batch large operations: Instead of one 20k-row insert, split into 1000-row batches
4. Consider transactions: Wrap related raw queries in $transaction:
await prisma.$transaction([
prisma.$executeRaw`DELETE FROM old_data WHERE created_at < NOW() - INTERVAL '1 year'`,
prisma.$executeRaw`VACUUM;` // PostgreSQL cleanup
]);### Debugging Raw Queries
Enable Prisma logging to see actual SQL sent to the database:
const prisma = new PrismaClient({
log: [
{ level: 'query', emit: 'stdout' },
{ level: 'info', emit: 'stdout' },
{ level: 'warn', emit: 'stdout' },
{ level: 'error', emit: 'stdout' },
],
});This shows the exact query and parameters sent, helping identify parameter serialization issues.
### Migration from Raw Queries
If you find yourself using raw queries frequently, consider:
1. Use Prisma's `$skip()` and `$take()` for pagination instead of raw LIMIT/OFFSET
2. Use `include` and `select` instead of complex JOINs in raw SQL
3. Use Prisma's aggregation API instead of raw COUNT/SUM queries
4. Use Prisma's groupBy for GROUP BY queries
This keeps more of your logic in Prisma where it's safer and type-checked.
P1013: The provided database string is invalid
The provided database string is invalid
P1000: Authentication failed against database server
Authentication failed against database server
P1010: User was denied access on the database
How to fix "P1010: User was denied access on the database" in Prisma
P5008: Usage exceeded, upgrade your plan (Accelerate)
How to fix "Usage exceeded, upgrade your plan" in Prisma Accelerate
P3021: Foreign keys cannot be created on this database
How to fix 'P3021: Foreign keys cannot be created on this database' in Prisma