Skip to content

fix: materialize typed-array vectors on store fetch paths#945

Draft
gorkem2020 wants to merge 4 commits into
CortexReach:masterfrom
gorkem2020:fix/typed-array-vectors-on-fetch
Draft

fix: materialize typed-array vectors on store fetch paths#945
gorkem2020 wants to merge 4 commits into
CortexReach:masterfrom
gorkem2020:fix/typed-array-vectors-on-fetch

Conversation

@gorkem2020

Copy link
Copy Markdown
Contributor

LanceDB returns vector columns as typed arrays (Float32Array) or Arrow vector objects, not plain JavaScript arrays. Three row-fetch paths in src/store.ts assumed otherwise:

  • fetchForCompaction used Array.isArray(row.vector) ? row.vector : [] to decide whether to keep the vector. Since Array.isArray is always false for a typed array, every fetched vector silently collapsed to an empty array.
  • bm25Search and lexicalFallbackSearch used a bare row.vector as number[] cast, which does not actually convert anything: the returned MemoryEntry.vector is a typed array pretending to be number[] at the type level, but not a real array at runtime.

This went unnoticed because the one existing consumer of fetchForCompaction, the background compactor, ignores the returned vectors entirely (its own clustering step never reads them). The bug surfaced through a downstream consolidation command that clusters rows by embedding similarity and got zero clusters back from a live dry-run, because every vector it saw had length zero.

getById and the other row-mapping call sites in this file were already unaffected, since they use Array.from(row.vector as Iterable<number>), which correctly converts a typed array via iteration.

Fix

All three affected sites now use the existing toNumberVector helper (already the pattern the primary vectorSearch path relies on), which accepts any array-like input (typed arrays, Arrow vectors, plain arrays), converts every element with Number(), and validates the result is entirely finite before returning it, falling back to [] only for genuinely absent or malformed vectors.

Test plan

  • Red-first: test/typed-array-vector-fetch.test.mjs stores a real vector into a temporary LanceDB table and asserts each of the three affected paths returns it intact. All three failed against the current code (fetchForCompaction returned a zero-length vector; bm25Search and lexicalFallbackSearch returned a non-Array value) and passed after the fix.
  • npm run build (tsc) clean.
  • Full local npm test chain green (one test skipped: a known host-side port 11434 conflict unrelated to this change).
  • test/typed-array-vector-fetch.test.mjs registered in both package.json's test script and scripts/ci-test-manifest.mjs (storage-and-schema group); manifest verification passes.

…al LanceDB round-trip

fetchForCompaction, bm25Search, and lexicalFallbackSearch each return a
typed array (Float32Array/Arrow vector) from LanceDB disguised as
number[] via an unchecked cast, or silently collapse it to [] when an
Array.isArray guard fails on it. All three tests store a real vector into
a temp LanceDB table and fail against current code before the fix.
fetchForCompaction used Array.isArray(row.vector) to decide whether to
keep or discard the vector; LanceDB returns vector columns as typed
arrays (Float32Array) or Arrow vectors, for which Array.isArray is always
false, so every fetched vector silently became []. bm25Search and
lexicalFallbackSearch had the same underlying problem in a different
shape: a bare 'row.vector as number[]' cast that never actually converts
the value, leaving a typed array masquerading as number[] at runtime.

consolidate is the first consumer of fetchForCompaction that actually
needs the vectors (compaction's own clustering ignored them), which is
why this went unnoticed until a live dry-run returned zero clusters.

Fixed all three call sites to use the existing toNumberVector helper
(src/store.ts:370), which vectorSearch's primary path already relies on:
it accepts any ArrayLike (typed arrays, Arrow vectors, plain arrays),
converts every element with Number(), and validates the result is all
finite before returning it, falling back to [] only for genuinely absent
or malformed vectors. getById and the other row-mapping sites in this
file already used the equivalent Array.from(row.vector as
Iterable<number>) pattern and were unaffected.
@gorkem2020
gorkem2020 force-pushed the fix/typed-array-vectors-on-fetch branch from 54749f5 to 9da228c Compare July 18, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant