How to fix Bind message supplies N parameters but prepared statement requires M in PostgreSQL
This error occurs when the number of parameters you bind to a prepared statement does not match the number of placeholders in the query. It usually happens when parameter placeholders ($1, $2) are accidentally quoted or when there's a mismatch between your prepared statement definition and the bind call.
Quick fix
Fixes Bind message supplies N parameters but prepared statement requires M
PREPARE stmt AS SELECT * FROM users WHERE name = '$1';- Check that parameter placeholders are not quoted
- Fix LIKE patterns to use unquoted parameters
- Verify parameter count matches between PREPARE and BIND
- Check for unescaped dollar signs in parameter placeholders
PREPARE stmt AS SELECT * FROM users WHERE name = '$1';Bind message supplies N parameters but prepared statement requires M
What this error means
PostgreSQL prepared statements work by separating query structure from parameters. When you create a prepared statement with placeholders like $1 and $2, PostgreSQL counts exactly how many parameters the query expects. When you later bind (execute) that statement, you must provide exactly that many parameters. If the counts don't match, PostgreSQL raises this error. The error message shows the number of parameters you supplied (N) versus the number the prepared statement requires (M). Common root causes include accidentally putting parameter placeholders inside quoted strings (where they're treated as literals, not placeholders), using parameters inside LIKE patterns with quotes, or mismatched parameter counts between the parse and bind phases of the PostgreSQL wire protocol.
Typical symptoms
- Prepared statement execution fails immediately when binding parameters
- Application logs show "Bind message supplies X parameters, but prepared statement requires Y"
- Query works fine without parameters, but fails when parameterized
- Error occurs inconsistently depending on which values are passed to the query
- Database drivers report protocol-level errors during statement execution
Common causes
- Parameter placeholder ($1, $2, etc.) is placed inside single quotes, causing PostgreSQL to treat it as a string literal instead of a placeholder (e.g., WHERE name = '$1')
- Using parameters inside LIKE pattern matching with quotes (e.g., WHERE column LIKE '%$1%')
- Parameters inside string concatenation contexts like INTERVAL '$1 ms' where PostgreSQL doesn't recognize them as placeholders
- Escaping the dollar sign in the parameter placeholder, causing it to be treated as a literal string
- Mismatch between prepared statement definition and bind call on the same connection - different queries cached with same placeholder counts
- Connection pool reusing prepared statements from different contexts with different parameter requirements
- Exceeding PostgreSQL's 65,535 parameter limit in bulk operations, resulting in an "error 65536 vs 0" type message
How to fix "Bind message supplies N parameters but prepared statement requires M"
Review your SQL query and ensure parameter placeholders ($1, $2, etc.) are NOT inside single quotes.
Incorrect:
PREPARE stmt AS SELECT * FROM users WHERE name = '$1';The $1 inside quotes is treated as a literal string, not a parameter placeholder.
Correct:
PREPARE stmt AS SELECT * FROM users WHERE name = $1;Parameter placeholders must be outside quotes to be recognized by PostgreSQL.
If using LIKE clauses, parameters must not be inside the string quotes.
Incorrect:
PREPARE search AS SELECT * FROM products WHERE description LIKE '%$1%';
-- When binding 1 parameter, PostgreSQL sees 0 placeholders (the $1 is a literal string)Correct:
PREPARE search AS SELECT * FROM products WHERE description LIKE '%' || $1 || '%';
-- Parameter $1 is now recognized outside the quotesUse string concatenation (||) to combine the LIKE pattern with your parameter.
Count the parameter placeholders ($1, $2, ...) in your prepared statement definition and ensure you provide exactly that many values when binding.
Example with 2 parameters:
PREPARE get_user AS SELECT * FROM users WHERE id = $1 AND status = $2;
-- This prepared statement requires exactly 2 parameters
EXECUTE get_user(123, 'active');
-- Correct: 2 values provided
EXECUTE get_user(123);
-- Error: only 1 value provided, but prepared statement requires 2Double-check that all placeholders are counted and all are provided.
Ensure the dollar sign in parameter placeholders is not escaped or quoted.
Incorrect (common in some ORMs or string builders):
PREPARE stmt AS SELECT * FROM logs WHERE data = '$1';
-- The escaped dollar sign is treated as a literal $ character
PREPARE stmt AS SELECT * FROM data WHERE value = "$1";
-- Double quotes make $1 a literal string in some contextsCorrect:
PREPARE stmt AS SELECT * FROM logs WHERE data = $1;
-- Plain $1 is recognized as a parameter placeholderPostgreSQL has a hard limit of 65,535 parameters per query. If you're building a very large INSERT or UPDATE with many parameters, split it into multiple queries.
Problematic (65,536 parameters - exceeds limit):
# Trying to insert 10,000 rows with 7 columns each = 70,000 parameters
values = [(id, name, email, ...) for i in range(10000)]
placeholders = ','.join(f'(${{i*7+1}}, ${{i*7+2}}, ...)' for i in range(10000))
cursor.execute(f'INSERT INTO users VALUES {placeholders}', values)Solution: Batch insert into chunks:
BATCH_SIZE = 1000
for i in range(0, len(values), BATCH_SIZE):
batch = values[i:i+BATCH_SIZE]
placeholders = ','.join(f'(${{j*7+1}}, ${{j*7+2}}, ...)' for j in range(len(batch)))
cursor.execute(f'INSERT INTO users VALUES {placeholders}', [v for row in batch for v in row])Use psql directly to test your prepared statement and verify the parameter count:
psql -U postgres -d your_database-- Test query with parameters unquoted
PREPARE test_stmt AS SELECT * FROM users WHERE id = $1 AND role = $2;
-- Should work - 2 parameters provided
EXECUTE test_stmt(1, 'admin');
-- Should fail - only 1 parameter
EXECUTE test_stmt(1);
-- Error: bind message supplies 1 parameters, but prepared statement requires 2If the statement works in psql but fails in your application, the issue is likely in how your driver is constructing or binding the parameters.
Advanced notes
This error operates at the PostgreSQL wire protocol level, which is why it can be cryptic for developers using ORMs and database drivers. The Parse phase (preparing the statement) determines how many parameters PostgreSQL expects by scanning for $N placeholders outside of quotes. The Bind phase (executing) then supplies the actual values. Mismatches between these phases cause the error. Some ORMs (like Sequelize, TypeORM) may hide this by caching prepared statements inappropriately across different queries on the same connection pool. If you encounter this sporadically in production but not development, suspect connection pooling issues where a stale prepared statement is being reused. Named prepared statements can exacerbate this if not properly scoped per statement name. For debugging, enable query logging in PostgreSQL (log_statement = 'all') to see the exact PARSE and BIND messages, though the wire protocol messages themselves show more detail than standard SQL logging.
Frequently asked questions
- What does "Bind message supplies N parameters but prepared statement requires M" mean?
- PostgreSQL prepared statements work by separating query structure from parameters. When you create a prepared statement with placeholders like $1 and $2, PostgreSQL counts exactly how many parameters the query expects. When you later bind (execute) that statement, you must provide exactly that many parameters.
- How do I fix "Bind message supplies N parameters but prepared statement requires M"?
- This error occurs when the number of parameters you bind to a prepared statement does not match the number of placeholders in the query. It usually happens when parameter placeholders ($1, $2) are accidentally quoted or when there's a mismatch between your prepared statement definition and the bind call.
- What causes "Bind message supplies N parameters but prepared statement requires M"?
- Parameter placeholder ($1, $2, etc.) is placed inside single quotes, causing PostgreSQL to treat it as a string literal instead of a placeholder (e.g., WHERE name = '$1') Using parameters inside LIKE pattern matching with quotes (e.g., WHERE column LIKE '%$1%') Parameters inside string concatenation contexts like INTERVAL '$1 ms' where PostgreSQL doesn't recognize them as placeholders
- Is the "Bind message supplies N parameters but prepared statement requires M" error serious?
- It is a medium-severity error โ more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- ๐PostgreSQL Official Documentation
- ๐PostgreSQL Docs - Message Formats (Bind)(Official Docs)
- ๐PostgreSQL Docs - Message Flow (Parse & Bind)(Official Docs)
- ๐PostgreSQL Docs - PREPARE Statement(Official Docs)