Skip to content

Non-finite vector components are accepted without error; resulting NULL / ±Infinity cosine distances sort ahead of the true nearest neighbor (v0.1.9) #325

Description

@JoeyLYZ666

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions