What happens
Measured on main at ad8ff24, one value at a time through each store's own code, on SQLite and PostgreSQL where a store supports both, with Qdrant 1.17.0 (REST and gRPC) and Milvus 2.6.24 and Milvus Lite. The value is a metadata value of an EpisodeEntry passed to MemMachine.add_episodes. The REST request takes only strings as metadata values, so this is reachable in process only.
| store |
NaN, inf, -inf |
integer outside signed 64 bits (2^63, -2^63 - 1, 2^64, 10^30) |
| episode store, SQLite |
stored |
stored |
| episode store, PostgreSQL |
refused (InvalidTextRepresentationError: token NaN is invalid) |
stored |
| segment store, SQLite |
stored |
stored |
| segment store, PostgreSQL |
refused |
stored |
SQLiteVectorStore, SQLiteVecVectorStore |
stored; an equality filter on NaN matches nothing |
stored; an equality filter on it raises OverflowError |
QdrantVectorStore, REST |
lost |
2^63 stored, and a filter on it answers 400; the others read back as floats |
QdrantVectorStore, gRPC |
refused (INVALID_ARGUMENT) |
refused (ValueError: Value out of range) |
MilvusVectorStore, 2.6.24 and Lite |
stored, and the record can no longer be read |
2^63 stored, and a filter on it raises; the others refused (DataNotMatchException) |
| semantic memory |
stored as its string form in a set id |
stored as its string form in a set id |
So MemMachine.add_episodes writes such an episode to the stores that keep the value and fails in the others.
Why
EpisodeEntry.metadata is dict[str, JsonValue] (packages/server/src/memmachine_server/common/episode_store/episode_model.py:34), and episodic memory copies every boolean, number, and string in it into the episode's filterable properties (packages/server/src/memmachine_server/episodic_memory/episodic_memory.py:225-229).
MemMachine.add_episodes writes the episode store, episodic memory, and semantic memory concurrently and raises the first failure once all of them return (packages/server/src/memmachine_server/main/memmachine.py:795-819).
- PostgreSQL JSONB has no token for non-finite numbers, and Qdrant and Milvus hold integers in 64 bits.
Expected
Building an EpisodeEntry whose metadata holds such a number raises, before any store writes.
Fix
#1792 checks EpisodeEntry's metadata values that are property values against the property value domain: finite floats and signed 64-bit integers. #1784 covers the vector stores' own handling of non-finite values.
🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.
What happens
Measured on
mainat ad8ff24, one value at a time through each store's own code, on SQLite and PostgreSQL where a store supports both, with Qdrant 1.17.0 (REST and gRPC) and Milvus 2.6.24 and Milvus Lite. The value is a metadata value of anEpisodeEntrypassed toMemMachine.add_episodes. The REST request takes only strings as metadata values, so this is reachable in process only.inf,-infInvalidTextRepresentationError: tokenNaNis invalid)SQLiteVectorStore,SQLiteVecVectorStoreOverflowErrorQdrantVectorStore, RESTQdrantVectorStore, gRPCINVALID_ARGUMENT)ValueError: Value out of range)MilvusVectorStore, 2.6.24 and LiteDataNotMatchException)So
MemMachine.add_episodeswrites such an episode to the stores that keep the value and fails in the others.Why
EpisodeEntry.metadataisdict[str, JsonValue](packages/server/src/memmachine_server/common/episode_store/episode_model.py:34), and episodic memory copies every boolean, number, and string in it into the episode's filterable properties (packages/server/src/memmachine_server/episodic_memory/episodic_memory.py:225-229).MemMachine.add_episodeswrites the episode store, episodic memory, and semantic memory concurrently and raises the first failure once all of them return (packages/server/src/memmachine_server/main/memmachine.py:795-819).Expected
Building an
EpisodeEntrywhose metadata holds such a number raises, before any store writes.Fix
#1792 checks
EpisodeEntry's metadata values that are property values against the property value domain: finite floats and signed 64-bit integers. #1784 covers the vector stores' own handling of non-finite values.🤖 Written by Claude Code (Claude Opus 5.5) on behalf of @edwinyyyu.