Skip to content

StateSnapshot puts the user metadata in possibly wrong place #6460

@spandan-sharma

Description

@spandan-sharma

Checked other resources

  • This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/).
  • 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
import json
from typing import Annotated, TypedDict

from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph, add_messages


class State(TypedDict):
    messages: Annotated[list[str], add_messages]


async def node1(state: State) -> State:
    print("Node1 executing:", state)
    return {"messages": ["node1"]}


async def main():
    graph = StateGraph(State)
    graph.add_node("node1", node1)
    graph.add_edge(START, "node1")
    graph.add_edge("node1", END)

    checkpointer = InMemorySaver()
    workflow = graph.compile(checkpointer=checkpointer)

    thread_id = "test-thread-1"
    config = {
        "metadata": {
            "foo": "bar",
        },
        "configurable": {
            "thread_id": thread_id,
        },
    }

    print("Running graph with RunnableConfig:")
    print(json.dumps(config, indent=2))
    await workflow.ainvoke({"messages": []}, config=config)
    print("\n" + "=" * 60)
    print("State history:")
    print("=" * 60 + "\n")

    get_state_config = {"configurable": {"thread_id": thread_id}}
    async for snapshot in workflow.aget_state_history(config=get_state_config):
        print("snapshot RunnableConfig:")
        print(snapshot.config)
        print("\nsnapshot CheckpointMetadata:")
        print(snapshot.metadata)
        print("\n" + "-" * 60 + "\n")


if __name__ == "__main__":
    asyncio.run(main())

Error Message and Stack Trace (if applicable)

Running graph with RunnableConfig:
{
  "metadata": {
    "foo": "bar"
  },
  "configurable": {
    "thread_id": "test-thread-1"
  }
}
Node1 executing: {'messages': []}

============================================================
State history:
============================================================

snapshot RunnableConfig:
{'configurable': {'thread_id': 'test-thread-1', 'checkpoint_ns': '', 'checkpoint_id': '1f0c4ac4-15e7-6ea6-8001-1150b7285acf'}}

snapshot CheckpointMetadata:
{'source': 'loop', 'step': 1, 'parents': {}, 'foo': 'bar'}

------------------------------------------------------------

snapshot RunnableConfig:
{'configurable': {'thread_id': 'test-thread-1', 'checkpoint_ns': '', 'checkpoint_id': '1f0c4ac4-15e6-6be6-8000-9923452d125c'}}

snapshot CheckpointMetadata:
{'source': 'loop', 'step': 0, 'parents': {}, 'foo': 'bar'}

------------------------------------------------------------

snapshot RunnableConfig:
{'configurable': {'thread_id': 'test-thread-1', 'checkpoint_ns': '', 'checkpoint_id': '1f0c4ac4-15df-6008-bfff-32845d236c59'}}

snapshot CheckpointMetadata:
{'source': 'input', 'step': -1, 'parents': {}, 'foo': 'bar'}

------------------------------------------------------------

Description

This is a run of a simple graph. I fetch the state history after the graph finishes. I've injected metadata foo=bar in the config. In each StateSnapshot in aget_state_history I get my metadata inside snapshot.metadata instead of snapshot.conifg['metadata'].

Documentation says snapshot.metadata:

class CheckpointMetadata(TypedDict, total=False):
    """Metadata associated with a checkpoint."""

    source: Literal["input", "loop", "update", "fork"]
    """The source of the checkpoint.
    [ ... ]
    """
    step: int
    """The step number of the checkpoint.
    [ ... ]
    """
    parents: dict[str, str]
    """The IDs of the parent checkpoints.

    Mapping from checkpoint namespace to checkpoint ID.
    """

so from that ^^^ I shouldn't be expecting to get my metadata there. snapshot.config OTOH is RunnableConfig the exact type where I had put my metadata while doing workflow.ainvoke. So I would also expect my metadata to be populated in snapshot.config['metadata'], but that's not the case.

Either this is a bug or the documentation needs to be clearer about what to expect where, what is the difference between snapshot.config['metadata'] and snapshot.metadata etc.

System Info

$ pip freeze | grep lang
langchain==1.0.7
langchain-classic==1.0.0
langchain-community==0.4.1
langchain-core==1.0.5
langchain-openai==1.0.3
langchain-text-splitters==1.0.0
langgraph==1.0.3
langgraph-checkpoint==3.0.1
langgraph-checkpoint-postgres==3.0.1
langgraph-checkpoint-sqlite==3.0.0
langgraph-prebuilt==1.0.4
langgraph-sdk==0.2.9
langsmith==0.4.43

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpendingawaiting review/confirmation by maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions