fix(langchain): Filter non-serializable state in Send for ShellToolMiddleware #34500
+191
−2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #34490
This PR fixes the
TypeError: Type is not msgpack serializable: Senderror that occurs when usingShellToolMiddlewarewithInMemorySavercheckpointer.Problem
When combining
ShellToolMiddlewarewithInMemorySaver()checkpointer, the agent fails with a serialization error. Users reported that the middleware works fine without checkpointing, but fails when persistence is enabled.Error Message:
Reproduction:
Root Cause Analysis
The issue is NOT that
Senditself isn't serializable - LangGraph has proper msgpack support forSendviaEXT_CONSTRUCTOR_POS_ARGS. The real problem is what's INSIDESend.arg.The Flow:
ShellToolMiddlewarestores_SessionResourcesin agent state_SessionResourcescontains non-serializable objects:subprocess.Popen(shell process handle)threading.Thread(output reader threads)queue.Queue(output queues)weakref.finalize(cleanup handler)model_to_tools(), the edge function createsSend("tools", ToolCallWithContext(state=state, ...))shell_session_resources) is passed toToolCallWithContextSendobject, it fails on the non-serializable contentsWhy UntrackedValue annotation doesn't help:
The
shell_session_resourcesfield is annotated withUntrackedValueandPrivateStateAttr, which should exclude it from checkpointing. However, when the entire state dict is passed intoSend.arg, these annotations are bypassed - the whole dict gets serialized including the non-serializable fields.Solution
Filter out known non-serializable state fields before passing to
Sendin themodel_to_tools()edge function.Changes in
libs/langchain_v1/langchain/agents/factory.py:Added
_NON_SERIALIZABLE_STATE_FIELDSconstant - a frozenset of field names known to contain non-serializable middleware stateAdded
_filter_serializable_state()helper function - filters out non-serializable fields from state dict before passing to SendModified
model_to_tools()edge - calls the filter before creatingToolCallWithContextBefore:
After:
Testing
All 28 existing shell tool tests pass:
Backward Compatibility
No breaking changes - This fix only affects internal state serialization. Tool execution behavior is completely unchanged. The filtered fields are middleware-internal state that tools don't need access to.
Design Decisions
Why filter by field name instead of trying to serialize:
_NON_SERIALIZABLE_STATE_FIELDSWhy filter at Send creation instead of in middleware:
model_to_tools()Future Considerations
UntrackedValue/PrivateStateAttrannotations at the serialization layer_NON_SERIALIZABLE_STATE_FIELDSRelated Issues