sqlite: excess bound parameters produce an opaque "column index out of range" error
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 122k
- Forks
- 37.3k
- Avg merge
- 4d 2h
- Merged PRs (30d)
- 283
Description
Version
v24.15.0 (also present on main)
Platform
Darwin 25.6.0 arm64 (platform-independent — pure BindParams logic)
Subsystem
sqlite
What steps will reproduce the bug?
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE t(a)');
const ins = db.prepare('INSERT INTO t VALUES (?)');
ins.run(1, 2); // ERR_SQLITE_ERROR, errcode 25, "column index out of range"
db.prepare('SELECT 1').get(5); // same
Any excess anonymous argument reproduces it, regardless of type — 2, 'x', and null all give the identical message.
How often does it reproduce? Is there a required condition?
Always, whenever the number of anonymous arguments exceeds the statement's sqlite3_bind_parameter_count().
What is the expected behavior? Why is that the expected behavior?
An error naming the actual problem — that more parameters were supplied than the statement accepts, ideally with both counts. Something like:
TypeError [ERR_INVALID_ARG_COUNT]: Statement accepts 1 parameter, but 2 were provided.
Two reasons this matters:
-
The message describes the wrong thing. "Column index out of range" is SQLite's wording for a binding index, but to a JS caller "column" reads as a table column, pointing them at their schema rather than their call site. Nothing in the message indicates an argument-count mismatch.
-
It's inconsistent with how the adjacent failure is reported. A wrong-type argument gets a precise Node-authored error:
ERR_INVALID_ARG_TYPE: Provided value cannot be bound to SQLite parameter 2.A wrong-count argument falls through to a raw SQLite error code. Both are caller mistakes in the same call, caught in the same function.
What do you see instead?
ERR_SQLITE_ERROR with errcode: 25 and message column index out of range.
Additional information
The anonymous-binding loop in StatementSync::BindParams (src/node_sqlite.cc) iterates args from anon_start to args.Length() without comparing that span against sqlite3_bind_parameter_count(), so the overflow surfaces from sqlite3_bind_* instead. param_count is already fetched a few lines above, inside the bare-named-params block. A pre-loop guard would cover every excess-argument case at once.
Worth deciding up front whether this should throw at all, or ignore extra arguments the way ordinary JS functions do. Throwing seems better for a database API, and it's the current behavior, so a guard would preserve semantics while fixing only the message. Note this would be a breaking change for anyone matching on ERR_SQLITE_ERROR/errcode 25, so it likely wants semver-major treatment.
Surfaced while reviewing #62008, which changes undefined handling in the same function; the two are independent.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/node_sqlite.cc at StatementSync::BindParams, reading the existing param_count handling and anonymous-binding loop. Reproduce the issue with the SQL snippets provided; done means excess anonymous arguments report a clear parameter-count error instead of SQLite errcode 25, without changing the existing binding behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js, sqlite
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100