What happens
Measured on main at ad8ff24: one record per value, upserted with a float property a of nan, inf, or -inf, then read back and filtered on.
| store |
upsert |
read back |
a = inf on the inf record |
SQLiteVectorStore, SQLiteVecVectorStore |
stored |
the value |
matches |
QdrantVectorStore, Qdrant 1.17.0 over REST |
succeeds |
the property is gone: the stored payload holds "a": null, and the record reads back with {} |
no match |
QdrantVectorStore, Qdrant 1.17.0 over gRPC |
raises grpc.aio.AioRpcError INVALID_ARGUMENT: cannot convert to json number |
— |
— |
MilvusVectorStore, Milvus 2.6.24 and Milvus Lite 3.2.1 |
succeeds |
every get or query that returns the record's properties raises ValueError: Property 'a' entry missing 'v' |
2.6.24: MilvusException: (code=1100, ... (_p_a == inf): two column comparison with JSON type is not supported; Lite: no match |
A filter value of nan matches nothing on the SQLite stores. On Milvus 2.6.24, a = nan fails like a = inf, and a = -inf fails with unexpected op: -. On Qdrant over REST, a = nan, a = inf, and a = -inf each match a record holding a = 2.
Why
- The SQLite stores keep the value in their type-tagged JSON column and compare it in SQL (
packages/server/src/memmachine_server/common/properties_json.py:33-63).
- The Qdrant store passes the value into the payload unchanged (
packages/server/src/memmachine_server/common/vector_store/qdrant_vector_store.py:253-269). Over REST the point is stored with null in its place, which the store reads back as an absent property (:282); a float equality filter is Range(gte=v, lte=v) (:161-170), which over REST with a non-finite bound matched a record holding 2. Over gRPC the server refuses the value, and the error is not one the store's batch-splitting retry catches (:317-331).
- The Milvus store writes the value into both the
properties JSON field and a dynamic filter field (packages/server/src/memmachine_server/common/vector_store/milvus_vector_store.py:177-198). The value comes back as null (properties read back as {"a": {"v": null, "t": "float"}}), and decode_properties refuses an entry without a value (packages/server/src/memmachine_server/common/properties_json.py:103-105), so the record cannot be read. Filter literals are rendered with repr (milvus_vector_store.py:80-81), giving nan, inf, and -inf, which Milvus 2.6.24 does not parse as numbers.
Expected
Every store answers a non-finite property value the same way, and every stored record stays readable and filterable.
Fix
A fix is in progress on feat/horizontal-scaling: the stores refuse a non-finite property value at write, as that branch already refuses a non-finite query vector or score threshold.
🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.
What happens
Measured on
mainat ad8ff24: one record per value, upserted with a float propertyaofnan,inf, or-inf, then read back and filtered on.a = infon theinfrecordSQLiteVectorStore,SQLiteVecVectorStoreQdrantVectorStore, Qdrant 1.17.0 over REST"a": null, and the record reads back with{}QdrantVectorStore, Qdrant 1.17.0 over gRPCgrpc.aio.AioRpcErrorINVALID_ARGUMENT: cannot convert to json numberMilvusVectorStore, Milvus 2.6.24 and Milvus Lite 3.2.1getorquerythat returns the record's properties raisesValueError: Property 'a' entry missing 'v'MilvusException: (code=1100, ... (_p_a == inf): two column comparison with JSON type is not supported; Lite: no matchA filter value of
nanmatches nothing on the SQLite stores. On Milvus 2.6.24,a = nanfails likea = inf, anda = -inffails withunexpected op: -. On Qdrant over REST,a = nan,a = inf, anda = -infeach match a record holdinga = 2.Why
packages/server/src/memmachine_server/common/properties_json.py:33-63).packages/server/src/memmachine_server/common/vector_store/qdrant_vector_store.py:253-269). Over REST the point is stored withnullin its place, which the store reads back as an absent property (:282); a float equality filter isRange(gte=v, lte=v)(:161-170), which over REST with a non-finite bound matched a record holding 2. Over gRPC the server refuses the value, and the error is not one the store's batch-splitting retry catches (:317-331).propertiesJSON field and a dynamic filter field (packages/server/src/memmachine_server/common/vector_store/milvus_vector_store.py:177-198). The value comes back asnull(propertiesread back as{"a": {"v": null, "t": "float"}}), anddecode_propertiesrefuses an entry without a value (packages/server/src/memmachine_server/common/properties_json.py:103-105), so the record cannot be read. Filter literals are rendered withrepr(milvus_vector_store.py:80-81), givingnan,inf, and-inf, which Milvus 2.6.24 does not parse as numbers.Expected
Every store answers a non-finite property value the same way, and every stored record stays readable and filterable.
Fix
A fix is in progress on
feat/horizontal-scaling: the stores refuse a non-finite property value at write, as that branch already refuses a non-finite query vector or score threshold.🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.