Filter by metadata in the Mongo Checkpointer is dead code. #351

Open
opened 2026-02-15 18:16:06 -05:00 by yindo · 2 comments
Owner

Originally created by @KDKHD on GitHub (Aug 29, 2025).

Checklist

  • I added a clear and descriptive title to this issue.
  • I searched the LangGraph.js documentation with the integrated search.
  • I used GitHub search and found no similar issues.
  • I am confident this is a bug in LangGraph.js, not in my own code.
  • The bug persists on the latest stable version of LangGraph (and the relevant integration package).

Summary

The MongoDB Checkpointer contains a bug in its metadata filtering logic. However, according to @nfcampos LangGraph core never uses metadata-based filtering, the buggy code is effectively dead code and does not result in any runtime errors.


Example

  • Persisting metadata: Metadata is stored as base64 when checkpoints are persisted → link
  • Filtering logic: Retrieval attempts to filter directly on metadata properties → link

Because the stored metadata is base64-encoded, the filter logic is incorrect and would never work as intended.


Impact

  • The metadata filter is broken and would fail if used.
  • In practice, LangGraph does not rely on metadata filtering, so the bug is dormant and causes no user-facing errors.
  • The relevant lines function as dead code under current usage.

Possible Resolutions

  1. Remove the metadata filter code if it is not part of the intended API.
  2. Fix the filter logic if metadata-based queries may be needed in future extensions.

System Info

N/A

Originally created by @KDKHD on GitHub (Aug 29, 2025). ### Checklist - [x] I added a clear and descriptive title to this issue. - [x] I searched the LangGraph.js documentation with the integrated search. - [x] I used GitHub search and found no similar issues. - [x] I am confident this is a bug in LangGraph.js, not in my own code. - [x] The bug persists on the latest stable version of LangGraph (and the relevant integration package). --- ### Summary The MongoDB Checkpointer contains a bug in its metadata filtering logic. However, according to @nfcampos LangGraph core never uses metadata-based filtering, the buggy code is effectively dead code and does not result in any runtime errors. --- ### Example - **Persisting metadata**: Metadata is stored as base64 when checkpoints are persisted → [link](https://github.com/langchain-ai/langgraphjs/blob/b6ae560b7925dcd7eeb76700efc4d0671b9c2831/libs/checkpoint-mongodb/src/index.ts#L236) - **Filtering logic**: Retrieval attempts to filter directly on metadata properties → [link](https://github.com/langchain-ai/langgraphjs/blob/b6ae560b7925dcd7eeb76700efc4d0671b9c2831/libs/checkpoint-mongodb/src/index.ts#L153-L155) Because the stored metadata is base64-encoded, the filter logic is incorrect and would never work as intended. --- ### Impact - The metadata filter is broken and would fail if used. - In practice, LangGraph does not rely on metadata filtering, so the bug is dormant and causes no user-facing errors. - The relevant lines function as dead code under current usage. --- ### Possible Resolutions 1. **Remove the metadata filter code** if it is not part of the intended API. 2. **Fix the filter logic** if metadata-based queries may be needed in future extensions. --- ### System Info N/A
yindo added the bugcheckpointer labels 2026-02-15 18:16:06 -05:00
Author
Owner

@SunHuawei commented on GitHub (Sep 29, 2025):

I’ve identified three possible fixes:

  1. write both the serialized metadata and a JSON shadow copy to enable efficient Mongo-side filtering
  2. drop the Mongo filter and handle metadata filtering in application code—which would force us to load and deserialize extra documents whenever callers provide metadata filters
  3. try to decode the base64 blob inside the Mongo query—which isn’t practical because Mongo doesn’t support that kind of decoding efficiently or safely.

I recommend the first option because it preserves fast database filtering and sets us up well for future metadata-heavy use cases.

Here's the change. And I can submit a PR if you agree with option 1:

diff --git a/libs/checkpoint-mongodb/src/index.ts b/libs/checkpoint-mongodb/src/index.ts
index 5a6f2d9..c1b7b42 100644
--- a/libs/checkpoint-mongodb/src/index.ts
+++ b/libs/checkpoint-mongodb/src/index.ts
@@ -151,7 +151,7 @@ export class MongoDBSaver extends BaseCheckpointSaver {
 
     if (filter) {
       Object.entries(filter).forEach(([key, value]) => {
-        query[`metadata.${key}`] = value;
+        query[`metadata_search.${key}`] = value;
       });
     }
 
@@ -234,6 +234,7 @@ export class MongoDBSaver extends BaseCheckpointSaver {
       parent_checkpoint_id: config.configurable?.checkpoint_id,
       type: checkpointType,
       checkpoint: serializedCheckpoint,
       metadata: serializedMetadata,
+      metadata_search: metadata,
     };
     const upsertQuery = {
       thread_id,
@SunHuawei commented on GitHub (Sep 29, 2025): I’ve identified three possible fixes: 1. write both the serialized metadata and a JSON shadow copy to enable efficient Mongo-side filtering 2. drop the Mongo filter and handle metadata filtering in application code—which would force us to load and deserialize extra documents whenever callers provide metadata filters 3. try to decode the base64 blob inside the Mongo query—which isn’t practical because Mongo doesn’t support that kind of decoding efficiently or safely. I recommend the first option because it preserves fast database filtering and sets us up well for future metadata-heavy use cases. Here's the change. And I can submit a PR if you agree with option 1: ```diff diff --git a/libs/checkpoint-mongodb/src/index.ts b/libs/checkpoint-mongodb/src/index.ts index 5a6f2d9..c1b7b42 100644 --- a/libs/checkpoint-mongodb/src/index.ts +++ b/libs/checkpoint-mongodb/src/index.ts @@ -151,7 +151,7 @@ export class MongoDBSaver extends BaseCheckpointSaver { if (filter) { Object.entries(filter).forEach(([key, value]) => { - query[`metadata.${key}`] = value; + query[`metadata_search.${key}`] = value; }); } @@ -234,6 +234,7 @@ export class MongoDBSaver extends BaseCheckpointSaver { parent_checkpoint_id: config.configurable?.checkpoint_id, type: checkpointType, checkpoint: serializedCheckpoint, metadata: serializedMetadata, + metadata_search: metadata, }; const upsertQuery = { thread_id, ```
Author
Owner

@KDKHD commented on GitHub (Sep 29, 2025):

Great suggestions @SunHuawei !

I have heard that the Langchain team is changing the BaseCheckpointSaver interface, so hopefully, with this change, there will be more clarity around how this filtering is expected to work.

  1. Write both the serialized metadata and a JSON shadow copy to enable efficient Mongo-side filtering

I don't think this is the most appropriate solution. The data that is currently being serialized is typed as any. It should not be the responsibility of the checkpointer implementation to try and do any filtering on objects that are not fully typed.

  1. drop the Mongo filter and handle metadata filtering in application code—which would force us to load and deserialize extra documents whenever callers provide metadata filters

I quite like this solution. It makes implementing the BaseCheckpointSaver easier, as filtering can be handled by an abstraction layer. This would work nicely since the data that is currently serialized is mostly untyped anyway, which means the filtering should happen in the same area of the code where the data is produced and where it is properly typed. This depends on the access patterns, though, and how much data the "unfiltered" queries return. We would need to ensure we don't cause memory issues by doing this.

  1. try to decode the base64 blob inside the Mongo query—which isn’t practical because Mongo doesn’t support that kind of decoding efficiently or safely.

Checkpointers can be implemented with various data storage mechanisms. Replicating a query that decodes the blob might not be possible in other data stores.

To the best of my knowledge, this filtering is never used, so outright deleting it would probably be best.

Whatever change is made, it needs to be replicated across all other implementations too to ensure consistency.

@KDKHD commented on GitHub (Sep 29, 2025): Great suggestions @SunHuawei ! I have heard that the Langchain team is changing the BaseCheckpointSaver interface, so hopefully, with this change, there will be more clarity around how this filtering is expected to work. > 1. Write both the serialized metadata and a JSON shadow copy to enable efficient Mongo-side filtering I don't think this is the most appropriate solution. The data that is currently being serialized is typed as `any`. It should not be the responsibility of the checkpointer implementation to try and do any filtering on objects that are not fully typed. > 2. drop the Mongo filter and handle metadata filtering in application code—which would force us to load and deserialize extra documents whenever callers provide metadata filters I quite like this solution. It makes implementing the BaseCheckpointSaver easier, as filtering can be handled by an abstraction layer. This would work nicely since the data that is currently serialized is mostly untyped anyway, which means the filtering should happen in the same area of the code where the data is produced and where it is properly typed. This depends on the access patterns, though, and how much data the "unfiltered" queries return. We would need to ensure we don't cause memory issues by doing this. > 4. try to decode the base64 blob inside the Mongo query—which isn’t practical because Mongo doesn’t support that kind of decoding efficiently or safely. Checkpointers can be implemented with various data storage mechanisms. Replicating a query that decodes the blob might not be possible in other data stores. To the best of my knowledge, this filtering is never used, so outright deleting it would probably be best. Whatever change is made, it needs to be replicated across all other implementations too to ensure consistency.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#351