Skip to content

rvf: RvfDatabase.query() throws misleading napi type error on options-object call form ({k}/{topK}/{limit}) #657

Description

@pacphi

Summary

RvfDatabase.query() in @ruvector/rvf@0.2.3 throws a misleading low-level N-API error when called with an options-object second argument ({k: 2}, {topK: 2}, or {limit: 2}), instead of either accepting the object form or validating the argument type before crossing the N-API boundary.

Environment

  • @ruvector/rvf@0.2.3 (current npm latest, no 0.2.4+ published as of this report; latest published 2026-07-05)
  • Node v26.4.0, macOS arm64, native @ruvector/rvf-node addon

How to reproduce

import { RvfDatabase } from '@ruvector/rvf';
const db = await RvfDatabase.create('./test.rvf', { dimensions: 8 });
await db.ingestBatch([{ id: 'doc1', vector: new Float32Array(8).fill(0), metadata: {} }]);

await db.query(new Float32Array(8), { k: 2 });      // throws
await db.query(new Float32Array(8), { topK: 2 });   // throws
await db.query(new Float32Array(8), { limit: 2 });  // throws
await db.query(new Float32Array(8), 2);              // works

Actual output (all three object forms)

RvfError: Backend initialization failed: Failed to convert napi value Object into
rust type `u32`

(error code 65281)

Reproduced live against the currently installed native addon.

Expected behavior

Either the object form is accepted (most vector-DB JS APIs — including, going by naming conventions used elsewhere in this same package, plausibly this one — accept a {k}-style options object), or the wrapper validates arguments in the TypeScript/JS layer and throws a clear, correctly-scoped error before crossing the N-API boundary. "Backend initialization failed" is actively misleading — it reads as a database-open-time failure, not an argument-type error on a specific call.

Root cause (confirmed by reading source)

dist/database.js declares query(vector, k, options) (line 109). dist/backend.js, NodeBackend.query() (line 149) passes k straight through to this.handle.query(vector, k, nativeOpts) with no type validation in the JS layer — an object passed as k reaches the native N-API boundary unchanged, where napi-rs's raw type-conversion error ("Failed to convert napi value Object into rust type u32") surfaces directly to the caller.

Sample use cases affected

  • k-nearest-neighbor query against a local .rvf vector store, e.g. serving a /api/search?mode=semantic-style endpoint where k is naturally expressed as part of an options object alongside filters

Workaround

Call with k as a bare positional number: db.query(vector, k).

Proposed fix

Accept both call signatures for ergonomics (query(vector, k) and query(vector, { k })), or at minimum validate the k argument's type in dist/database.js/dist/backend.js and throw a typed RvfError (e.g. InvalidArgument) carrying the actual accepted signature in its message, before calling into the native addon. Document the accepted signature explicitly in the README/type defs, which currently don't make it unambiguous.

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