Current Behavior
sqlite-vec accepts vectors containing non-finite float32 components (NaN, +Inf) without any error, in both vec0 tables and the scalar distance functions. The resulting cosine distances are then non-finite, and in a MATCH query SQLite's ordering places the broken rows AHEAD of the true nearest neighbor.
Corpus in a vec0 cosine table: id1 = [0.9, 0.1] (the true nearest to query [1, 0], distance 0.0062), id2 = [NaN, 1], id3 = [Inf, 1] — all three inserts succeed:
SELECT id, distance FROM t WHERE v MATCH vec_f32('[1,0]') AND k = 3
ORDER BY distance;
-- (2, NULL), (3, NULL), (1, 0.006116...)
The two corrupt rows rank FIRST (SQLite sorts NULL before every value), and the actual nearest neighbor is displaced to the last position. Scalar functions are equally affected: vec_distance_cosine('[1,0]', '[NaN,1]') = NULL, and pairs involving +Inf return ±Infinity (see companion report on small-norm vectors for the same functions returning ±Inf on finite input).
Expected Behavior
Either reject non-finite components at insert/argument time with a clear error (the precedent exists in the ecosystem: pgvector rejects with "NaN not allowed in vector", Qdrant treats rejection as the correct boundary behavior in #9378), or define and document their distance semantics — but a NULL distance that sorts as better than every finite distance turns any threshold or top-k use into silently wrong results.
Steps to Reproduce
reproduce.py --output observed.json (Python 3 + sqlite-vec only, in-memory DB, cleans up nothing since it creates no files). It inserts the three rows, runs the MATCH query and the scalar calls, prints everything, and exits non-zero unless the nearest row ranks first and all distances are finite in [0, 2]. Deterministic; observed.json is from an actual run, executed twice with identical results.
reproduce.py
Context (Environment)
- sqlite-vec v0.1.9 via
vec_version(), bundled through the sqlite-vec Python package, SQLite 3.x, Python 3.12, Windows 11
- In-memory database;
distance_metric=cosine table, no other extensions
The issue was found while checking that adding rows cannot change other rows' nearest-neighbor identity or score finiteness. This is the input-validation side of the float32-robustness cluster reported separately for small-norm vectors (NULL/±Inf there arise from finite input); here the trigger is non-finite input, accepted without error.
Current Behavior
sqlite-vec accepts vectors containing non-finite float32 components (NaN, +Inf) without any error, in both
vec0tables and the scalar distance functions. The resulting cosine distances are then non-finite, and in aMATCHquery SQLite's ordering places the broken rows AHEAD of the true nearest neighbor.Corpus in a
vec0cosine table: id1 =[0.9, 0.1](the true nearest to query[1, 0], distance 0.0062), id2 =[NaN, 1], id3 =[Inf, 1]— all three inserts succeed:The two corrupt rows rank FIRST (SQLite sorts NULL before every value), and the actual nearest neighbor is displaced to the last position. Scalar functions are equally affected:
vec_distance_cosine('[1,0]', '[NaN,1]')= NULL, and pairs involving+Infreturn ±Infinity (see companion report on small-norm vectors for the same functions returning ±Inf on finite input).Expected Behavior
Either reject non-finite components at insert/argument time with a clear error (the precedent exists in the ecosystem: pgvector rejects with "NaN not allowed in vector", Qdrant treats rejection as the correct boundary behavior in #9378), or define and document their distance semantics — but a NULL distance that sorts as better than every finite distance turns any threshold or top-k use into silently wrong results.
Steps to Reproduce
reproduce.py --output observed.json(Python 3 + sqlite-vec only, in-memory DB, cleans up nothing since it creates no files). It inserts the three rows, runs the MATCH query and the scalar calls, prints everything, and exits non-zero unless the nearest row ranks first and all distances are finite in [0, 2]. Deterministic;observed.jsonis from an actual run, executed twice with identical results.reproduce.py
Context (Environment)
vec_version(), bundled through thesqlite-vecPython package, SQLite 3.x, Python 3.12, Windows 11distance_metric=cosinetable, no other extensionsThe issue was found while checking that adding rows cannot change other rows' nearest-neighbor identity or score finiteness. This is the input-validation side of the float32-robustness cluster reported separately for small-norm vectors (NULL/±Inf there arise from finite input); here the trigger is non-finite input, accepted without error.