Measured
node:sqlite's bundled SQLite is built without FTS5 until Node 22.16.0:
| Node |
CREATE VIRTUAL TABLE t USING fts5(x) |
| 22.13.0 |
fails |
| 22.15.0 |
fails |
| 22.16.0 |
succeeds |
| 22.17.0, 22.20.0, 22.23.2 |
succeeds |
The declared floor is 22.13.0 (raised there because node:sqlite itself is flagged below it, #586). So on 22.13–22.15 the index exists but its full-text path does not, and every query falls back to LIKE.
How it surfaced
Pinning CI's floor job to the exact declared minimum made test/index-db.test.ts fail:
× index-db: --no-index fallback returns identical rows
> agrees with the index across the query matrix
→ expected false to be true
That is expect(handle.fts).toBe(true) at the top of the case. It passes on 22.23 and cannot pass on 22.13.
The product is fine; the claim is not
The fallback is real and covered — there is a sibling case, agrees when FTS5 is unavailable, so the LIKE path is not a different feature, and expectFallbackAgrees runs the same query matrix through both. So a user on 22.13 gets correct answers by a slower route.
What is wrong is that a test asserts a runtime capability as though it were guaranteed, on a floor where it is not. It reads as "FTS5 is available" when what is true is "FTS5 is available on the Node this happened to run on". That is the same shape as the floor defect itself: an assumption about the runtime that nothing checked until the floor was actually executed.
What closes it
Decide which is intended and make the code say it:
- Floor stays 22.13.0. Then FTS5 is optional, and the test must assert agreement in whichever mode the runtime provides rather than asserting the mode. Document that repositories on 22.13–22.15 use the LIKE path, and consider whether
doctor should say so — a user comparing query timings against the README's numbers deserves to know.
- Floor moves to 22.16.0. Then FTS5 is guaranteed and the assertion is legitimate. This costs users on 22.13–22.15, and the reason would be performance rather than correctness.
Either way scripts/check-engines.mjs's new built-in check should learn this: it currently maps node:sqlite to 22.13.0, which is right for the module and wrong for the feature the code relies on.
Found by pinning the CI matrix to the declared floor.
Measured
node:sqlite's bundled SQLite is built without FTS5 until Node 22.16.0:CREATE VIRTUAL TABLE t USING fts5(x)The declared floor is 22.13.0 (raised there because
node:sqliteitself is flagged below it, #586). So on 22.13–22.15 the index exists but its full-text path does not, and every query falls back toLIKE.How it surfaced
Pinning CI's floor job to the exact declared minimum made
test/index-db.test.tsfail:That is
expect(handle.fts).toBe(true)at the top of the case. It passes on 22.23 and cannot pass on 22.13.The product is fine; the claim is not
The fallback is real and covered — there is a sibling case, agrees when FTS5 is unavailable, so the LIKE path is not a different feature, and
expectFallbackAgreesruns the same query matrix through both. So a user on 22.13 gets correct answers by a slower route.What is wrong is that a test asserts a runtime capability as though it were guaranteed, on a floor where it is not. It reads as "FTS5 is available" when what is true is "FTS5 is available on the Node this happened to run on". That is the same shape as the floor defect itself: an assumption about the runtime that nothing checked until the floor was actually executed.
What closes it
Decide which is intended and make the code say it:
doctorshould say so — a user comparing query timings against the README's numbers deserves to know.Either way
scripts/check-engines.mjs's new built-in check should learn this: it currently mapsnode:sqliteto 22.13.0, which is right for the module and wrong for the feature the code relies on.Found by pinning the CI matrix to the declared floor.