This error occurs when executing a prepared statement with a mismatched number of parameters. The SQL query expects M parameter placeholders (like $1, $2) but your application is providing N values instead. This commonly happens when parameter placeholders are accidentally placed inside string literals or when parameters are missing from the query.
This error indicates a parameter count mismatch between your prepared statement and the bind message sent by the PostgreSQL client driver. When PostgreSQL prepares a statement, it counts the parameter placeholders ($1, $2, etc.) in your SQL. When you execute the statement, the client sends a bind message with values. If the number of values doesn't match the number of placeholders PostgreSQL found during preparation, this error occurs. The error can manifest as: - "bind message supplies 0 parameters, but prepared statement requires 2" - "bind message supplies 2 parameters, but prepared statement requires 1" - "bind message supplies 1 parameters, but prepared statement requires 0" The mismatch usually stems from placeholders being unintentionally quoted or from the application passing the wrong number of arguments.
Parameter placeholders must NOT be inside string literals. Check your SQL for any placeholders wrapped in quotes.
WRONG - placeholder inside quotes:
SELECT * FROM users WHERE email = '$1'RIGHT - placeholder outside quotes:
SELECT * FROM users WHERE email = $1Search your code for patterns like '$1', '$2', etc. and move them outside quotes.
When using INTERVAL or string patterns, don't put placeholders inside quotes. Use string concatenation or type casting instead.
WRONG:
SELECT NOW() + INTERVAL '$1 ms'
SELECT * FROM users WHERE name LIKE '%$1%'RIGHT:
SELECT NOW() + ($1 || ' ms')::INTERVAL
SELECT * FROM users WHERE name LIKE '%' || $1 || '%'For INTERVAL with duration, cast properly:
SELECT NOW() + ($1::TEXT || ' ms')::INTERVALCount all $N placeholders in your SQL query and ensure your application passes exactly that many values.
// Example: SQL with 3 placeholders
const sql = 'SELECT * FROM users WHERE id = $1 AND status = $2 AND role = $3';
// Must pass exactly 3 values
const result = await db.query(sql, [userId, 'active', 'admin']);
// ✓ Correct: 3 placeholders, 3 values
// This would fail:
const result = await db.query(sql, [userId, 'active']); // ✗ Only 2 values!For debugging, log the SQL and parameter count:
console.log('SQL:', sql);
console.log('Placeholder count:', (sql.match(/\$(\d+)/g) || []).length);
console.log('Values provided:', values.length);In some string escape scenarios, placeholders get accidentally quoted:
// WRONG - escaped quotes make it a literal string:
const query = `SELECT * FROM users WHERE email = '$1'`;
// RIGHT - no quotes around placeholder:
const query = `SELECT * FROM users WHERE email = $1`;
// WRONG - in object/string concatenation:
const query = 'SELECT * FROM users WHERE data = \'' + param + '\'';
// RIGHT - use parameterized query:
const query = 'SELECT * FROM users WHERE data = $1';
const result = await db.query(query, [param]);Check template literals and string concatenation for quoted placeholders.
If using an ORM (Prisma, Sequelize, TypeORM, etc.), enable query logging to see what SQL is being generated:
For Prisma:
const prisma = new PrismaClient({
log: ['query', 'error'],
});For node-postgres:
const query = { text: 'SELECT * FROM users WHERE id = $1', values: [userId] };
console.log('Query:', query); // Log to inspect
await client.query(query);Compare the logged query against your expected placeholders and parameters. ORMs sometimes generate queries differently than expected.
PostgreSQL limits prepared statements to 65535 parameters. For bulk operations (like inserting many records), batch them:
WRONG - too many parameters in one statement:
// This tries to insert 100,000 records with 2 params each = 200,000 parameters!
const values = records.flatMap((r) => [r.name, r.email]);
const placeholders = values.map((_, i) => \`($\{i + 1}, $\{i + 2})\`);
const sql = \`INSERT INTO users (name, email) VALUES $\{placeholders.join(',')}\`;RIGHT - batch into chunks:
const batchSize = 1000; // Adjust based on column count
for (let i = 0; i < records.length; i += batchSize) {
const batch = records.slice(i, i + batchSize);
const placeholders = batch
.map((_, idx) => \`($\{idx * 2 + 1}, $\{idx * 2 + 2})\`)
.join(',');
const values = batch.flatMap((r) => [r.name, r.email]);
const sql = \`INSERT INTO users (name, email) VALUES $\{placeholders}\`;
await client.query(sql, values);
}Or use a library like pg-format for safer bulk inserts.
Run your query directly in psql (PostgreSQL command-line tool) to see if the issue is with your SQL or your driver/ORM:
psql -U postgres -d your_database
-- Test a simple parameterized query
PREPARE test_query (TEXT, TEXT) AS SELECT * FROM users WHERE email = $1 AND status = $2;
-- Execute with parameters
EXECUTE test_query('[email protected]', 'active');If it works in psql but fails in your application, the issue is in your driver/ORM configuration. If it fails in both, the issue is in your SQL.
Prepared Statements & Security: Using parameterized queries (prepared statements) protects against SQL injection. The parameter mismatch error is PostgreSQL's way of enforcing type safety—make sure you're using parameterized queries correctly, not building SQL strings manually.
Driver-Specific Issues: Different PostgreSQL drivers have different placeholder syntaxes:
- PostgreSQL native: uses $1, $2, etc.
- node-postgres (pg): uses $1, $2
- psycopg2 (Python): uses %s (not numbered)
- JDBC (Java): uses ?
- libpq (C): uses $1, $2
Make sure your driver's placeholder syntax matches your query syntax.
Framework-Specific Quirks: Some ORMs build queries dynamically and may not handle all SQL patterns correctly:
- Prisma: Uses $1 internally but handles parameterization for you; usually won't see this error unless using raw queries
- Sequelize: May have issues with custom bind parameters in raw queries
- TypeORM: Similar to Sequelize; check custom query implementations
Connection Pooling: If using PgBouncer or similar, ensure the pooler correctly forwards prepared statements to the backend. Some pooling modes (statement pooling vs. transaction pooling) handle prepared statements differently.
PostgreSQL Version Compatibility: Very old PostgreSQL versions (< 9.0) had different parameter handling. If using an old server, upgrade if possible or review driver documentation for compatibility.
ERROR: syntax error at end of input
Syntax error at end of input in PostgreSQL
Multidimensional arrays must have sub-arrays with matching dimensions
Multidimensional arrays must have sub-arrays with matching dimensions
ERROR: value too long for type character varying
Value too long for type character varying
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL
ERROR 42501: must be owner of table
How to fix "must be owner of table" in PostgreSQL