msgpack deserialization with strictmap_key=False #354

Closed
opened 2026-02-20 17:38:03 -05:00 by yindo · 1 comment
Owner

Originally created by @pperliti on GitHub (Dec 12, 2024).

Checked other resources

  • This is a bug, not a usage question. For questions, please use GitHub Discussions.
  • I added a clear and detailed title that summarizes the issue.
  • I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.

Example Code

import asyncio
from typing import Annotated, TypedDict

from langgraph.constants import END
from langgraph.graph import StateGraph
from pydantic import BaseModel

from app.config.settings import get_settings
from app.utils.redis_checkpointer import AsyncRedisSaver

class CitationBroker(BaseModel):
    map_idx_to_utt: dict[int, int]

class AgentState(TypedDict):
    citation_brokers: list[CitationBroker]


def f1(state):
    cit_b = CitationBroker(
    map_idx_to_utt={
        1: 1,
        2: 2,
        3: 3
    })
    """
    With 
    map_idx_to_utt={
        '1': 1,
        '2': 2,
        '3': 3
    })
    and 
    
    class CitationBroker(BaseModel):
    map_idx_to_utt: dict[str, int]
    
    works
    """
    print(str(cit_b)) # not None
    return {
        "citation_brokers": state.get('citation_brokers', []) + [cit_b],
    }

def ask_human_node(state):
    print("get user input")

builder = StateGraph(AgentState)
builder.add_node("node_1", f1)
builder.add_node("ask_human_node", ask_human_node)
builder.set_entry_point("node_1")
builder.add_edge("ask_human_node", "node_1")
builder.add_edge("node_1", "ask_human_node")

settings = get_settings()
async def main():
    async with AsyncRedisSaver.from_url(settings.CACHE_REDIS_ENDPOINT) as memory:
        graph = builder.compile(checkpointer=memory, interrupt_before=["ask_human_node"])
        thread = {
            "configurable": {
                "thread_id": "1"
            }
        }

        async for event in graph.astream_events({
            "citation_brokers": [],
        }, config=thread, version="v2"):
            pass

        snapshot = await graph.aget_state(thread)
        print(str(snapshot.values['citation_brokers'][0]))  # None

asyncio.run(main())

Error Message and Stack Trace (if applicable)

File "msgpack\\_unpacker.pyx", line 194, in msgpack._cmsgpack.unpackb ValueError: int is not allowed for map key when strict_map_key=True

Description

I upgraded libraries from langgraph 0.2.19 to 0.2.58 and langgraph-checkpoint from 1.0.9 to 2.0.8.
I'm using a REDIS checkpointer as detailed in the official guide.
I'm serializing a TypedDict which contains Pydantic V2 objects as values (keys are strings). Each of this Pydantic V2 objects contains a simple Python dict() (whose keys are numeric).

When I try to deserialize the Pydantic object I get the following error:

File "msgpack\_unpacker.pyx", line 194, in msgpack._cmsgpack.unpackb ValueError: int is not allowed for map key when strict_map_key=True

Setting strict_map_key=False inside jsonplus.py solves the issue, but this implies cloning jsonplus.py just to set strict_map_key=False.

Indeed at line 210 of jsonplus.py I find:

elif type_ == "msgpack":
            return msgpack.unpackb(
                data_, ext_hook=_msgpack_ext_hook, strict_map_key=False
            )

but at line 482 of jsonplus.py:

elif code == EXT_PYDANTIC_V2:
        try:
            tup = msgpack.unpackb(data, ext_hook=_msgpack_ext_hook) # lacks of strict_map_key=False
            # module, name, kwargs, method
            cls = getattr(importlib.import_module(tup[0]), tup[1])
            try:
                return cls(**tup[2])
            except Exception:
                return cls.model_construct(**tup[2])
        except Exception:
            return

Any advice on how should I fix the problem? In the meantime I reverted to previous version of libraries (which solves the issue).
Thanks in advance.

System Info

Python 3.12.7 (tags/v3.12.7:0b05ead, Oct 1 2024, 03:06:41) [MSC v.1941 64 bit (AMD64)] on win32

Originally created by @pperliti on GitHub (Dec 12, 2024). ### Checked other resources - [X] This is a bug, not a usage question. For questions, please use GitHub Discussions. - [X] I added a clear and detailed title that summarizes the issue. - [X] I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example). - [X] I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue. ### Example Code ```python import asyncio from typing import Annotated, TypedDict from langgraph.constants import END from langgraph.graph import StateGraph from pydantic import BaseModel from app.config.settings import get_settings from app.utils.redis_checkpointer import AsyncRedisSaver class CitationBroker(BaseModel): map_idx_to_utt: dict[int, int] class AgentState(TypedDict): citation_brokers: list[CitationBroker] def f1(state): cit_b = CitationBroker( map_idx_to_utt={ 1: 1, 2: 2, 3: 3 }) """ With map_idx_to_utt={ '1': 1, '2': 2, '3': 3 }) and class CitationBroker(BaseModel): map_idx_to_utt: dict[str, int] works """ print(str(cit_b)) # not None return { "citation_brokers": state.get('citation_brokers', []) + [cit_b], } def ask_human_node(state): print("get user input") builder = StateGraph(AgentState) builder.add_node("node_1", f1) builder.add_node("ask_human_node", ask_human_node) builder.set_entry_point("node_1") builder.add_edge("ask_human_node", "node_1") builder.add_edge("node_1", "ask_human_node") settings = get_settings() async def main(): async with AsyncRedisSaver.from_url(settings.CACHE_REDIS_ENDPOINT) as memory: graph = builder.compile(checkpointer=memory, interrupt_before=["ask_human_node"]) thread = { "configurable": { "thread_id": "1" } } async for event in graph.astream_events({ "citation_brokers": [], }, config=thread, version="v2"): pass snapshot = await graph.aget_state(thread) print(str(snapshot.values['citation_brokers'][0])) # None asyncio.run(main()) ``` ### Error Message and Stack Trace (if applicable) ```shell File "msgpack\\_unpacker.pyx", line 194, in msgpack._cmsgpack.unpackb ValueError: int is not allowed for map key when strict_map_key=True ``` ### Description I upgraded libraries from **langgraph** 0.2.19 to **0.2.58** and **langgraph-checkpoint** from 1.0.9 to **2.0.8**. I'm using a REDIS checkpointer as detailed in [the official guide](https://langchain-ai.github.io/langgraph/how-tos/persistence_redis/). I'm serializing a TypedDict which contains Pydantic V2 objects as values (keys are strings). Each of this Pydantic V2 objects contains a simple Python dict() (whose keys are _numeric_). When I try to deserialize the Pydantic object I get the following error: > File "msgpack\\_unpacker.pyx", line 194, in msgpack._cmsgpack.unpackb ValueError: int is not allowed for map key when strict_map_key=True Setting `strict_map_key=False` inside _jsonplus.py_ solves the issue, but this implies cloning _jsonplus.py_ just to set `strict_map_key=False`. Indeed at line 210 of _jsonplus.py_ I find: ``` elif type_ == "msgpack": return msgpack.unpackb( data_, ext_hook=_msgpack_ext_hook, strict_map_key=False ) ``` but at line 482 of _jsonplus.py_: ``` elif code == EXT_PYDANTIC_V2: try: tup = msgpack.unpackb(data, ext_hook=_msgpack_ext_hook) # lacks of strict_map_key=False # module, name, kwargs, method cls = getattr(importlib.import_module(tup[0]), tup[1]) try: return cls(**tup[2]) except Exception: return cls.model_construct(**tup[2]) except Exception: return ``` Any advice on how should I fix the problem? In the meantime I reverted to previous version of libraries (which solves the issue). Thanks in advance. ### System Info Python 3.12.7 (tags/v3.12.7:0b05ead, Oct 1 2024, 03:06:41) [MSC v.1941 64 bit (AMD64)] on win32
yindo closed this issue 2026-02-20 17:38:04 -05:00
Author
Owner

@vbarda commented on GitHub (Dec 12, 2024):

Thanks for reporting -- the issue should be fixed in langgraph-checkpoint 2.0.9

@vbarda commented on GitHub (Dec 12, 2024): Thanks for reporting -- the issue should be fixed in langgraph-checkpoint 2.0.9
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#354