tool_use ids were found without tool_result blocks immediately after #724

Closed
opened 2026-02-20 17:41:27 -05:00 by yindo · 10 comments
Owner

Originally created by @viv01 on GitHub (Jun 14, 2025).

Hello langgraph team,

Problem statement

I am trying to run a agent based on information from the langgraph website, and i am stuck at this error. I am out of ideas on this one. below is the code and the error

I am trying to store memories in a postgres database (distance between cities and my travel preferences). I also created my own basic mcp server which can read my google calendar to get my meeting details. finally i am running this agent so that it can plan my meeting days according to my memories.

ENVIRONMENT

i am working on windows 11, VSCODE, postgres installed locally

$ python -m langchain_core.sys_info

System Information
------------------
> OS:  Windows
> OS Version:  10.0.26100
> Python Version:  3.12.0 (tags/v3.12.0:0fb18b0, Oct  2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)]

LangGraph version

$ pip list | grep -E "(langgraph|langchain)"
langchain                          0.3.24
langchain-anthropic                0.3.12
langchain-community                0.3.22
langchain-core                     0.3.55
langchain-mcp-adapters             0.1.4
langchain-openai                   0.3.14
langchain-text-splitters           0.3.8
langgraph                          0.4.8
langgraph-checkpoint               2.0.26
langgraph-checkpoint-postgres      2.0.21
langgraph-prebuilt                 0.2.2
langgraph-sdk                      0.1.61

For installing postgres locally i ran these steps

Download postgressql for windows
https://www.enterprisedb.com/downloads/postgres-postgresql-downloads

port - 5432

Server [localhost]: localhost
Database [postgres]: postgres
Port [5432]:
Username [postgres]: postgres
Password for user postgres:

#################################################################

Install the langgraph postgres driver

pip install -U "psycopg[binary,pool]" langgraph langgraph-checkpoint-postgres\

i am also attaching an image showing my postgres langraph database and tables (working correctly)

Image

WHAT IS WORKING

a. I am succesfully able to store memories in my postgres from this agent ( in the call_model funtion below)
b. I am also able to successfully get the event details from my mcp server (response below)
{"events":[{"summary":"Weekly report + lawson","start":"2016-06-17T20:00:00+05:30"},{"summary":"Check houses today","start":"2016-06-18T12:00:00+05:30"},{"summary":"Selfie","start":"2016-06-18T23:00:00+05:30"},{"summary":"Raspberry Pi - Fundamentals Unleashed","start":"2016-06-19T11:00:00+05:30"},{"summary":"Pack n keep return products - myntra","start":"2016-06-19T22:00:00+05:30"},{"summary":"Samsung vr coupon, tell samsung improvements","start":"2016-06-20T10:00:00+05:30"},{"summary":"Pay 30 to auto guy","start":"2016-06-24T10:00:00+05:30"},{"summary":"Give ad on facebook 3bhk, call usa 2bhk owner","start":"2016-06-25T10:30:00+05:30"},{"summary":"Return snapdeal","start":"2016-06-26T16:00:00+05:30"},{"summary":"Talk to vimala ulises","start":"2016-06-27T16:00:00+05:30"}]}

MY CODE (EDITED TODAY TO ADD THE CONFIG PART WHICH WAS MISSING WHEN I OPENED THE TICKET

import uuid

from langchain_core.runnables import RunnableConfig
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.checkpoint.postgres import PostgresSaver
from langgraph.store.postgres import PostgresStore
from langgraph.store.base import BaseStore

from langchain.tools import tool
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.messages import ToolMessage, ToolCall
import requests

llm = init_chat_model(model="anthropic:claude-3-5-haiku-latest")

DB_URI = "postgresql://postgres:password123@localhost:5432/postgres?sslmode=disable"

with (PostgresStore.from_conn_string(DB_URI) as store, PostgresSaver.from_conn_string(DB_URI) as checkpointer,):

    @tool
    def get_google_calendar_events() -> str:

        """Fetches upcoming events from the user's Google Calendar."""
        # print(">>> bb11")
        # try:
        #     response = requests.get("http://localhost:8000/events/next_week")
        #     response.raise_for_status()
        #     events = response.json().get("next_week_events", [])
        #     return "\n".join([f"{e['start']}: {e['summary']}" for e in events])
        # except Exception as e:
        #     return f"Error fetching events: {e}"
        return "abc"
        
    tools = [get_google_calendar_events]
    llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)

    def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore,):
        user_id = config["configurable"]["user_id"]
        namespace = ("memories", user_id)
        memories = store.search(namespace, query=str(state["messages"][-1].content))
        info = "\n".join([d.value["data"] for d in memories])
        system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

        # Store new memories if the user asks the model to remember
        last_message = state["messages"][-1]
        
        store.put(namespace, str(uuid.uuid4()), {"data": last_message.content})  

        print(system_msg)
        print("************************")
        print(state["messages"])

        return {"messages": [llm_with_tools.invoke(state["messages"])]}

   
    graph_builder = StateGraph(MessagesState)

    graph_builder.add_node("call_model", call_model)
    tool_node = ToolNode(tools=tools)
    graph_builder.add_node("tools", tool_node)

    graph_builder.add_conditional_edges(
        "call_model",
        tools_condition,
    )
    # Any time a tool is called, we return to the chatbot to decide the next step
    graph_builder.add_edge("tools", "call_model")
    graph_builder.add_edge(START, "call_model")
        
    graph = graph_builder.compile(
        checkpointer=checkpointer,
        store=store,
    )

    config = {
        "configurable": {
            "thread_id": "6",
            "user_id": "rachna1",
        }
    }

    for chunk in graph.stream(
        {"messages": [{"role": "user", "content": "Give me a summary of my data and my meetings next week"}]},
        config,
        stream_mode="values",
    ):
        chunk["messages"][-1].pretty_print()
BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages.6: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01CX8qs29zPLgHdzGcPi6b51. Each `tool_use` block must have a corresponding `tool_result` block in the next message.'}}
During task with name 'call_model' and id '5d6c3d4c-cb7c-b64c-8392-cc2db5b7ea6c'

the below image shows the error in my GUI. It fails after the human message as shown in the image. the image also shows the data in my store table , so you can cross relate it with the output

Image

This is the last HumanMessage after which the code fails

[
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='f88fa4ee-67cc-4618-8b7c-f336e68a6e29'), 
    AIMessage(
        content=[
            {'text': "Let me help you with that. I'll first retrieve your upcoming Google Calendar events to get a summary of your meetings next week.", 'type': 'text'}, 
            {'id': 'toolu_01929FqgEB2iGNcnVTUobATB', 'input': {}, 'name': 'get_google_calendar_events', 'type': 'tool_use'}
        ], 
        additional_kwargs={}, 
        response_metadata={
            'id': 'msg_01CJSmvuxKSHRLfuy34LbvBX', 
            'model': 'claude-3-5-haiku-20241022', 
            'stop_reason': 'tool_use', 
            'stop_sequence': None, 
            'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 552, 'output_tokens': 67, 'service_tier': 'standard'}, 
            'model_name': 'claude-3-5-haiku-20241022'
        }, 
        id='run-aaab78d0-86dc-4779-b9df-a16c3974b9c0-0', 
        tool_calls=[
            {'name': 'get_google_calendar_events', 'args': {}, 'id': 'toolu_01929FqgEB2iGNcnVTUobATB', 'type': 'tool_call'}
        ], 
        usage_metadata={
            'input_tokens': 552, 
            'output_tokens': 67, 
            'total_tokens': 619, 
            'input_token_details': {
                'cache_creation': 0, 
                'cache_read': 0
            }
        }
    ), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='f6d0cf35-c9f8-4c1a-a9a3-b63ba513894b'), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='7b3eb21f-46ec-4f08-a3db-576c326c56d1'), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='ba7b2b1c-1f71-4282-b1d8-d9ed9eebebb7'), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='2f379945-6edb-4552-9b69-4c20434f3887'), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='2b7d8201-bead-4b9b-8af9-e1316d98d84c'), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='de2fcd7f-d8ed-4473-9283-e2bcd6452685'), 
    HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='0e156dee-b165-45d1-9f26-04b4a8300c92')
]
Originally created by @viv01 on GitHub (Jun 14, 2025). Hello langgraph team, ### Problem statement I am trying to run a agent based on information from the langgraph website, and i am stuck at this error. I am out of ideas on this one. below is the code and the error I am trying to store memories in a postgres database (distance between cities and my travel preferences). I also created my own basic mcp server which can read my google calendar to get my meeting details. finally i am running this agent so that it can plan my meeting days according to my memories. ### ENVIRONMENT i am working on windows 11, VSCODE, postgres installed locally ``` $ python -m langchain_core.sys_info System Information ------------------ > OS: Windows > OS Version: 10.0.26100 > Python Version: 3.12.0 (tags/v3.12.0:0fb18b0, Oct 2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)] ``` LangGraph version ``` $ pip list | grep -E "(langgraph|langchain)" langchain 0.3.24 langchain-anthropic 0.3.12 langchain-community 0.3.22 langchain-core 0.3.55 langchain-mcp-adapters 0.1.4 langchain-openai 0.3.14 langchain-text-splitters 0.3.8 langgraph 0.4.8 langgraph-checkpoint 2.0.26 langgraph-checkpoint-postgres 2.0.21 langgraph-prebuilt 0.2.2 langgraph-sdk 0.1.61 ``` For installing postgres locally i ran these steps ``` Download postgressql for windows https://www.enterprisedb.com/downloads/postgres-postgresql-downloads port - 5432 Server [localhost]: localhost Database [postgres]: postgres Port [5432]: Username [postgres]: postgres Password for user postgres: ################################################################# Install the langgraph postgres driver pip install -U "psycopg[binary,pool]" langgraph langgraph-checkpoint-postgres\ ``` i am also attaching an image showing my postgres langraph database and tables (working correctly) ![Image](https://github.com/user-attachments/assets/8e5480d8-000e-4134-a0a8-af0325822536) ### WHAT IS WORKING a. I am succesfully able to store memories in my postgres from this agent ( in the call_model funtion below) b. I am also able to successfully get the event details from my mcp server (response below) `{"events":[{"summary":"Weekly report + lawson","start":"2016-06-17T20:00:00+05:30"},{"summary":"Check houses today","start":"2016-06-18T12:00:00+05:30"},{"summary":"Selfie","start":"2016-06-18T23:00:00+05:30"},{"summary":"Raspberry Pi - Fundamentals Unleashed","start":"2016-06-19T11:00:00+05:30"},{"summary":"Pack n keep return products - myntra","start":"2016-06-19T22:00:00+05:30"},{"summary":"Samsung vr coupon, tell samsung improvements","start":"2016-06-20T10:00:00+05:30"},{"summary":"Pay 30 to auto guy","start":"2016-06-24T10:00:00+05:30"},{"summary":"Give ad on facebook 3bhk, call usa 2bhk owner","start":"2016-06-25T10:30:00+05:30"},{"summary":"Return snapdeal","start":"2016-06-26T16:00:00+05:30"},{"summary":"Talk to vimala ulises","start":"2016-06-27T16:00:00+05:30"}]}` ### MY CODE (EDITED TODAY TO ADD THE CONFIG PART WHICH WAS MISSING WHEN I OPENED THE TICKET ``` import uuid from langchain_core.runnables import RunnableConfig from langchain.chat_models import init_chat_model from langgraph.graph import StateGraph, MessagesState, START from langgraph.checkpoint.postgres import PostgresSaver from langgraph.store.postgres import PostgresStore from langgraph.store.base import BaseStore from langchain.tools import tool from langgraph.prebuilt import ToolNode, tools_condition from langchain_core.messages import ToolMessage, ToolCall import requests llm = init_chat_model(model="anthropic:claude-3-5-haiku-latest") DB_URI = "postgresql://postgres:password123@localhost:5432/postgres?sslmode=disable" with (PostgresStore.from_conn_string(DB_URI) as store, PostgresSaver.from_conn_string(DB_URI) as checkpointer,): @tool def get_google_calendar_events() -> str: """Fetches upcoming events from the user's Google Calendar.""" # print(">>> bb11") # try: # response = requests.get("http://localhost:8000/events/next_week") # response.raise_for_status() # events = response.json().get("next_week_events", []) # return "\n".join([f"{e['start']}: {e['summary']}" for e in events]) # except Exception as e: # return f"Error fetching events: {e}" return "abc" tools = [get_google_calendar_events] llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False) def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore,): user_id = config["configurable"]["user_id"] namespace = ("memories", user_id) memories = store.search(namespace, query=str(state["messages"][-1].content)) info = "\n".join([d.value["data"] for d in memories]) system_msg = f"You are a helpful assistant talking to the user. User info: {info}" # Store new memories if the user asks the model to remember last_message = state["messages"][-1] store.put(namespace, str(uuid.uuid4()), {"data": last_message.content}) print(system_msg) print("************************") print(state["messages"]) return {"messages": [llm_with_tools.invoke(state["messages"])]} graph_builder = StateGraph(MessagesState) graph_builder.add_node("call_model", call_model) tool_node = ToolNode(tools=tools) graph_builder.add_node("tools", tool_node) graph_builder.add_conditional_edges( "call_model", tools_condition, ) # Any time a tool is called, we return to the chatbot to decide the next step graph_builder.add_edge("tools", "call_model") graph_builder.add_edge(START, "call_model") graph = graph_builder.compile( checkpointer=checkpointer, store=store, ) config = { "configurable": { "thread_id": "6", "user_id": "rachna1", } } for chunk in graph.stream( {"messages": [{"role": "user", "content": "Give me a summary of my data and my meetings next week"}]}, config, stream_mode="values", ): chunk["messages"][-1].pretty_print() ``` ``` BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages.6: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01CX8qs29zPLgHdzGcPi6b51. Each `tool_use` block must have a corresponding `tool_result` block in the next message.'}} During task with name 'call_model' and id '5d6c3d4c-cb7c-b64c-8392-cc2db5b7ea6c' ``` the below image shows the error in my GUI. It fails after the human message as shown in the image. the image also shows the data in my store table , so you can cross relate it with the output ![Image](https://github.com/user-attachments/assets/572b2db4-4eed-4c4a-af8e-d586c99406c7) This is the last HumanMessage after which the code fails ``` [ HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='f88fa4ee-67cc-4618-8b7c-f336e68a6e29'), AIMessage( content=[ {'text': "Let me help you with that. I'll first retrieve your upcoming Google Calendar events to get a summary of your meetings next week.", 'type': 'text'}, {'id': 'toolu_01929FqgEB2iGNcnVTUobATB', 'input': {}, 'name': 'get_google_calendar_events', 'type': 'tool_use'} ], additional_kwargs={}, response_metadata={ 'id': 'msg_01CJSmvuxKSHRLfuy34LbvBX', 'model': 'claude-3-5-haiku-20241022', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 552, 'output_tokens': 67, 'service_tier': 'standard'}, 'model_name': 'claude-3-5-haiku-20241022' }, id='run-aaab78d0-86dc-4779-b9df-a16c3974b9c0-0', tool_calls=[ {'name': 'get_google_calendar_events', 'args': {}, 'id': 'toolu_01929FqgEB2iGNcnVTUobATB', 'type': 'tool_call'} ], usage_metadata={ 'input_tokens': 552, 'output_tokens': 67, 'total_tokens': 619, 'input_token_details': { 'cache_creation': 0, 'cache_read': 0 } } ), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='f6d0cf35-c9f8-4c1a-a9a3-b63ba513894b'), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='7b3eb21f-46ec-4f08-a3db-576c326c56d1'), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='ba7b2b1c-1f71-4282-b1d8-d9ed9eebebb7'), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='2f379945-6edb-4552-9b69-4c20434f3887'), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='2b7d8201-bead-4b9b-8af9-e1316d98d84c'), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='de2fcd7f-d8ed-4473-9283-e2bcd6452685'), HumanMessage(content='Give me a summary of my data and my meetings next week', additional_kwargs={}, response_metadata={}, id='0e156dee-b165-45d1-9f26-04b4a8300c92') ] ```
yindo closed this issue 2026-02-20 17:41:27 -05:00
Author
Owner

@foie0222 commented on GitHub (Jun 16, 2025):

Hi @viv01! 👋

Nice for taking the time to report this issue. I can see you're experiencing a challenging problem with the tool chain execution.

To help the maintainers investigate this more effectively, could you consider updating your issue to include a few additional details? This would make it much easier for others to reproduce and fix the problem:

📋 Issue Template Requirements

I noticed this issue might not have used the standard issue template. LangGraph follows the same issue guidelines as LangChain, which includes a helpful checklist to ensure all necessary information is provided. When creating a new issue, GitHub should present you with a template that includes:

  • A descriptive title that summarizes the specific problem
  • Confirmation that you've searched for similar issues
  • Verification that this occurs with the latest stable version
  • Documentation and resource checks
  • A self-contained, minimal reproducible example

Using the issue template helps maintainers understand and address problems much more quickly!

🔧 Reproducible Example

The current code example requires PostgreSQL setup, which makes it harder for maintainers to reproduce. Would it be possible to provide:

  1. A self-contained example that others can copy and run immediately
  2. Using InMemoryStore instead of PostgresStore for easier reproduction
  3. A complete, runnable script (the current example is missing the config variable definition)

Here's a simplified example format that might help:

# All imports and setup
from langgraph.store.memory import InMemoryStore
# ... other imports

# Complete, runnable code that reproduces the error
config = RunnableConfig(configurable={"user_id": "test_user"})
# ... rest of the example

🔍 Environment Details

Adding version information would be super helpful:

pip list | grep -E "(langgraph|langchain)"

💡 Debugging Help

In the meantime, you might want to try:

  • Using create_react_agent from langgraph.prebuilt as a workaround
  • Adding system messages explicitly to your LLM calls
  • Checking if the issue persists with InMemoryStore

tool chain issues can be quite complex to debug! The maintainers will be able to help much more effectively with a reproducible example. 🙏

Would you be able to update the issue with these details when you have a chance?

@foie0222 commented on GitHub (Jun 16, 2025): Hi @viv01! 👋 Nice for taking the time to report this issue. I can see you're experiencing a challenging problem with the tool chain execution. To help the maintainers investigate this more effectively, could you consider updating your issue to include a few additional details? This would make it much easier for others to reproduce and fix the problem: ## 📋 Issue Template Requirements I noticed this issue might not have used the standard issue template. LangGraph follows the same issue guidelines as LangChain, which includes a helpful checklist to ensure all necessary information is provided. When creating a new issue, GitHub should present you with a template that includes: - ✅ A descriptive title that summarizes the specific problem - ✅ Confirmation that you've searched for similar issues - ✅ Verification that this occurs with the latest stable version - ✅ Documentation and resource checks - ✅ A self-contained, minimal reproducible example Using the issue template helps maintainers understand and address problems much more quickly! ## 🔧 Reproducible Example The current code example requires PostgreSQL setup, which makes it harder for maintainers to reproduce. Would it be possible to provide: 1. **A self-contained example** that others can copy and run immediately 2. **Using InMemoryStore instead of PostgresStore** for easier reproduction 3. **A complete, runnable script** (the current example is missing the `config` variable definition) Here's a simplified example format that might help: ```python # All imports and setup from langgraph.store.memory import InMemoryStore # ... other imports # Complete, runnable code that reproduces the error config = RunnableConfig(configurable={"user_id": "test_user"}) # ... rest of the example ``` ## 🔍 Environment Details Adding version information would be super helpful: ```bash pip list | grep -E "(langgraph|langchain)" ``` ## 💡 Debugging Help In the meantime, you might want to try: - Using `create_react_agent` from `langgraph.prebuilt` as a workaround - Adding system messages explicitly to your LLM calls - Checking if the issue persists with InMemoryStore tool chain issues can be quite complex to debug! The maintainers will be able to help much more effectively with a reproducible example. 🙏 Would you be able to update the issue with these details when you have a chance?
Author
Owner

@viv01 commented on GitHub (Jun 16, 2025):

Hi @foie0222,

Thank you for taking the time to get back on this. I have edited the description and added some more details as asked.

I did not get any template when i opened the issue, but i have added more details now.

I will try your suggestions and update the results when i am able to. Please look at the new details in the description till then.

Thanks and Regards.

@viv01 commented on GitHub (Jun 16, 2025): Hi @foie0222, Thank you for taking the time to get back on this. I have edited the description and added some more details as asked. I did not get any template when i opened the issue, but i have added more details now. I will try your suggestions and update the results when i am able to. Please look at the new details in the description till then. Thanks and Regards.
Author
Owner

@foie0222 commented on GitHub (Jun 17, 2025):

Hi @viv01,

Thanks for sharing the detailed logs. I tried reproducing this with your exact code and it worked fine in my environment, which led me to investigate further. I believe I've identified the root cause of this error. This appears to be a checkpoint data inconsistency issue rather than a LangGraph bug.

My Test Results

When I ran your code, it executed successfully:

================================ Human Message =================================
Give me a summary of my data and my meetings next week

================================== Ai Message ==================================
[Tool call to get_google_calendar_events]

================================= Tool Message =================================
Name: get_google_calendar_events
abc

================================== Ai Message ==================================
I apologize, but it seems there might be an issue retrieving your calendar events...

The fact that it works in fresh environments but fails in yours strongly suggests checkpoint corruption.

The Problem

The error tool_use ids were found without tool_result blocks immediately after occurs because your PostgreSQL checkpoint contains an AIMessage with tool_calls that doesn't have a corresponding ToolMessage immediately following it.

What Likely Happened

  1. During a previous execution, an AIMessage with tool_calls was saved to the checkpoint
  2. The corresponding ToolMessage was either not saved or corrupted in the database
  3. When LangGraph restored the conversation state, it loaded an incomplete message sequence
  4. Claude API received a tool_use block without the required tool_result block immediately after

Quick Fix

Use a fresh thread_id to avoid the corrupted checkpoint:

config = {
    "configurable": {
        "thread_id": f"fresh_{uuid.uuid4().hex[:8]}",  # New thread_id
        "user_id": "your_user_id",
    }
}

Database Cleanup (if needed)

-- Clear checkpoints for the problematic thread
DELETE FROM checkpoints 
WHERE thread_id = '6' AND checkpoint_ns = 'rachna1';

This explains why the issue doesn't reproduce with fresh installations—it's not a code issue but corrupted checkpoint data in your specific PostgreSQL database.

Hope this helps! Let me know if using a new thread_id resolves the issue.

@foie0222 commented on GitHub (Jun 17, 2025): Hi @viv01, Thanks for sharing the detailed logs. I tried reproducing this with your exact code and it worked fine in my environment, which led me to investigate further. I believe I've identified the root cause of this error. This appears to be a **checkpoint data inconsistency issue** rather than a LangGraph bug. ### My Test Results When I ran your code, it executed successfully: ``` ================================ Human Message ================================= Give me a summary of my data and my meetings next week ================================== Ai Message ================================== [Tool call to get_google_calendar_events] ================================= Tool Message ================================= Name: get_google_calendar_events abc ================================== Ai Message ================================== I apologize, but it seems there might be an issue retrieving your calendar events... ``` The fact that it works in fresh environments but fails in yours strongly suggests checkpoint corruption. ### The Problem The error `tool_use ids were found without tool_result blocks immediately after` occurs because your PostgreSQL checkpoint contains an `AIMessage` with `tool_calls` that doesn't have a corresponding `ToolMessage` immediately following it. ### What Likely Happened 1. During a previous execution, an `AIMessage` with `tool_calls` was saved to the checkpoint 2. The corresponding `ToolMessage` was either not saved or corrupted in the database 3. When LangGraph restored the conversation state, it loaded an incomplete message sequence 4. Claude API received a `tool_use` block without the required `tool_result` block immediately after ### Quick Fix Use a fresh `thread_id` to avoid the corrupted checkpoint: ```python config = { "configurable": { "thread_id": f"fresh_{uuid.uuid4().hex[:8]}", # New thread_id "user_id": "your_user_id", } } ``` ### Database Cleanup (if needed) ```sql -- Clear checkpoints for the problematic thread DELETE FROM checkpoints WHERE thread_id = '6' AND checkpoint_ns = 'rachna1'; ``` This explains why the issue doesn't reproduce with fresh installations—it's not a code issue but corrupted checkpoint data in your specific PostgreSQL database. Hope this helps! Let me know if using a new `thread_id` resolves the issue.
Author
Owner

@viv01 commented on GitHub (Jun 17, 2025):

Hi @foie0222 - I tested with a different thread_id and it worked now !! Thank you for the in depth explanation for the root cause.

I just need one more clarification.

I am attaching an image. Each checkpoint as i can see in the table, is based on a thread_id. It does not contain the user_id/checkpoint_ns.

so this query did not delete anything.

DELETE FROM checkpoints 
WHERE thread_id = '6' AND checkpoint_ns = 'rachna1';

was the checkpoints table, supposed to have the value 'rachna1' under checkpoint_ns column ?

Image

@viv01 commented on GitHub (Jun 17, 2025): Hi @foie0222 - I tested with a different thread_id and **it worked now** !! Thank you for the in depth explanation for the root cause. I just need one more clarification. I am attaching an image. Each checkpoint as i can see in the table, is based on a thread_id. It does not contain the user_id/checkpoint_ns. so this query did not delete anything. ``` DELETE FROM checkpoints WHERE thread_id = '6' AND checkpoint_ns = 'rachna1'; ``` was the checkpoints table, supposed to have the value 'rachna1' under checkpoint_ns column ? ![Image](https://github.com/user-attachments/assets/568d0caa-7ecf-4a6f-81f6-570b4817abc3)
Author
Owner

@viv01 commented on GitHub (Jun 17, 2025):

I wanted to highlight something else as well. In the code that runs successfully, i checked the checkpoints table and below are the rows that got printed. In none of these rows do i find a 'tool_result' block as mentioned in the error message that i was getting.

{
    "step": 38,
    "source": "input",
    "writes": {
        "__start__": {
            "messages": [
                {
                    "role": "user",
                    "content": "make a comprehensive travel plan for my meetings this week"
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 39,
    "source": "loop",
    "writes": null,
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 40,
    "source": "loop",
    "writes": {
        "call_model": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "AIMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "run-bc058d9b-ad01-4c51-9698-158e2c3c760d-0",
                        "type": "ai",
                        "content": [
                            {
                                "text": "I'll help you create a comprehensive travel plan. First, I'll check your calendar events:",
                                "type": "text"
                            },
                            {
                                "id": "toolu_01QgmJaZ87icSKYqoKAVEfan",
                                "name": "get_google_calendar_events",
                                "type": "tool_use",
                                "input": {}
                            }
                        ],
                        "tool_calls": [
                            {
                                "id": "toolu_01QgmJaZ87icSKYqoKAVEfan",
                                "args": {},
                                "name": "get_google_calendar_events",
                                "type": "tool_call"
                            }
                        ],
                        "usage_metadata": {
                            "input_tokens": 2509,
                            "total_tokens": 2560,
                            "output_tokens": 51,
                            "input_token_details": {
                                "cache_read": 0,
                                "cache_creation": 0
                            }
                        },
                        "response_metadata": {
                            "id": "msg_01ERr4mCtLhSM6BPbvoajNEi",
                            "model": "claude-3-5-haiku-20241022",
                            "usage": {
                                "input_tokens": 2509,
                                "service_tier": "standard",
                                "output_tokens": 51,
                                "cache_read_input_tokens": 0,
                                "cache_creation_input_tokens": 0
                            },
                            "model_name": "claude-3-5-haiku-20241022",
                            "stop_reason": "tool_use",
                            "stop_sequence": null
                        },
                        "invalid_tool_calls": []
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 41,
    "source": "loop",
    "writes": {
        "tools": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "ToolMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "23a11961-6515-445c-8a07-8842ad6c803b",
                        "name": "get_google_calendar_events",
                        "type": "tool",
                        "status": "success",
                        "content": "{'dateTime': '2025-06-18T11:00:00+05:30', 'timeZone': 'Asia/Kolkata'}: meeting",
                        "tool_call_id": "toolu_01QgmJaZ87icSKYqoKAVEfan"
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 42,
    "source": "loop",
    "writes": {
        "call_model": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "AIMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "run-5063d472-c59b-4603-9bf0-19033d098d06-0",
                        "type": "ai",
                        "content": [
                            {
                                "text": "I'll also retrieve your transportation preferences:",
                                "type": "text"
                            },
                            {
                                "id": "toolu_01GbXo3cU51iXdc1cCj9LzNt",
                                "name": "fetch_my_memories",
                                "type": "tool_use",
                                "input": {}
                            }
                        ],
                        "tool_calls": [
                            {
                                "id": "toolu_01GbXo3cU51iXdc1cCj9LzNt",
                                "args": {},
                                "name": "fetch_my_memories",
                                "type": "tool_call"
                            }
                        ],
                        "usage_metadata": {
                            "input_tokens": 2618,
                            "total_tokens": 2657,
                            "output_tokens": 39,
                            "input_token_details": {
                                "cache_read": 0,
                                "cache_creation": 0
                            }
                        },
                        "response_metadata": {
                            "id": "msg_01J1QPYeKR1h1BWFx2EvvYgG",
                            "model": "claude-3-5-haiku-20241022",
                            "usage": {
                                "input_tokens": 2618,
                                "service_tier": "standard",
                                "output_tokens": 39,
                                "cache_read_input_tokens": 0,
                                "cache_creation_input_tokens": 0
                            },
                            "model_name": "claude-3-5-haiku-20241022",
                            "stop_reason": "tool_use",
                            "stop_sequence": null
                        },
                        "invalid_tool_calls": []
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 43,
    "source": "loop",
    "writes": {
        "tools": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "ToolMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "9aacf1e0-a50a-4ba7-ae61-634f1303d331",
                        "name": "fetch_my_memories",
                        "type": "tool",
                        "status": "success",
                        "content": "My name is Rachna. I live in delhi. When i travel to and from gurugram, my preferred mode of transport is metro between the times of 9am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car. When i travel to and from noida, my preferred mode of transport is metro between the times of 10am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car.",
                        "tool_call_id": "toolu_01GbXo3cU51iXdc1cCj9LzNt"
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 44,
    "source": "loop",
    "writes": {
        "call_model": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "AIMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "run-6087f78c-957e-40d0-8d9d-b2e93aeee6db-0",
                        "type": "ai",
                        "content": "Comprehensive Travel Plan:\n\nScheduled Meeting:\n- Date: June 18, 2025\n- Time: 11:00 AM\n- Location: Not specified\n\nTravel Recommendations:\n1. Transportation Preference:\n   - Morning (9-11 AM): Prefer Metro\n   - Evening (6-8 PM): Prefer Metro\n   - Other times: Prefer personal car\n\nTravel Preparation:\n- Since the meeting is at 11:00 AM, this falls within your preferred metro time\n- Recommended transportation: Metro\n- Suggested departure time: 10:00 AM (allowing buffer time)\n\nTravel Checklist:\n1. Check metro route to meeting location\n2. Prepare metro card/fare\n3. Plan route and estimated travel time\n4. Backup transportation (personal car) if metro is inconvenient\n\nAdditional Recommendations:\n- Confirm exact meeting location to finalize travel details\n- Check metro schedule and potential alternate routes\n- Prepare necessary documents/materials for the meeting\n\nLimitations and Next Steps:\n- Meeting location is not specified\n- Only one meeting is currently scheduled\n- Recommend confirming more details about the meeting\n\nWould you like me to help you gather more specific information about the meeting location or transportation details?",
                        "tool_calls": [],
                        "usage_metadata": {
                            "input_tokens": 2787,
                            "total_tokens": 3068,
                            "output_tokens": 281,
                            "input_token_details": {
                                "cache_read": 0,
                                "cache_creation": 0
                            }
                        },
                        "response_metadata": {
                            "id": "msg_019FGguc7h4umcNuE5R3BCGe",
                            "model": "claude-3-5-haiku-20241022",
                            "usage": {
                                "input_tokens": 2787,
                                "service_tier": "standard",
                                "output_tokens": 281,
                                "cache_read_input_tokens": 0,
                                "cache_creation_input_tokens": 0
                            },
                            "model_name": "claude-3-5-haiku-20241022",
                            "stop_reason": "end_turn",
                            "stop_sequence": null
                        },
                        "invalid_tool_calls": []
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
@viv01 commented on GitHub (Jun 17, 2025): I wanted to highlight something else as well. In the code that runs successfully, i checked the checkpoints table and below are the rows that got printed. **In none of these rows do i find a 'tool_result' block as mentioned in the error message that i was getting.** ``` { "step": 38, "source": "input", "writes": { "__start__": { "messages": [ { "role": "user", "content": "make a comprehensive travel plan for my meetings this week" } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 39, "source": "loop", "writes": null, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 40, "source": "loop", "writes": { "call_model": { "messages": [ { "id": [ "langchain", "schema", "messages", "AIMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "run-bc058d9b-ad01-4c51-9698-158e2c3c760d-0", "type": "ai", "content": [ { "text": "I'll help you create a comprehensive travel plan. First, I'll check your calendar events:", "type": "text" }, { "id": "toolu_01QgmJaZ87icSKYqoKAVEfan", "name": "get_google_calendar_events", "type": "tool_use", "input": {} } ], "tool_calls": [ { "id": "toolu_01QgmJaZ87icSKYqoKAVEfan", "args": {}, "name": "get_google_calendar_events", "type": "tool_call" } ], "usage_metadata": { "input_tokens": 2509, "total_tokens": 2560, "output_tokens": 51, "input_token_details": { "cache_read": 0, "cache_creation": 0 } }, "response_metadata": { "id": "msg_01ERr4mCtLhSM6BPbvoajNEi", "model": "claude-3-5-haiku-20241022", "usage": { "input_tokens": 2509, "service_tier": "standard", "output_tokens": 51, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0 }, "model_name": "claude-3-5-haiku-20241022", "stop_reason": "tool_use", "stop_sequence": null }, "invalid_tool_calls": [] } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 41, "source": "loop", "writes": { "tools": { "messages": [ { "id": [ "langchain", "schema", "messages", "ToolMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "23a11961-6515-445c-8a07-8842ad6c803b", "name": "get_google_calendar_events", "type": "tool", "status": "success", "content": "{'dateTime': '2025-06-18T11:00:00+05:30', 'timeZone': 'Asia/Kolkata'}: meeting", "tool_call_id": "toolu_01QgmJaZ87icSKYqoKAVEfan" } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 42, "source": "loop", "writes": { "call_model": { "messages": [ { "id": [ "langchain", "schema", "messages", "AIMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "run-5063d472-c59b-4603-9bf0-19033d098d06-0", "type": "ai", "content": [ { "text": "I'll also retrieve your transportation preferences:", "type": "text" }, { "id": "toolu_01GbXo3cU51iXdc1cCj9LzNt", "name": "fetch_my_memories", "type": "tool_use", "input": {} } ], "tool_calls": [ { "id": "toolu_01GbXo3cU51iXdc1cCj9LzNt", "args": {}, "name": "fetch_my_memories", "type": "tool_call" } ], "usage_metadata": { "input_tokens": 2618, "total_tokens": 2657, "output_tokens": 39, "input_token_details": { "cache_read": 0, "cache_creation": 0 } }, "response_metadata": { "id": "msg_01J1QPYeKR1h1BWFx2EvvYgG", "model": "claude-3-5-haiku-20241022", "usage": { "input_tokens": 2618, "service_tier": "standard", "output_tokens": 39, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0 }, "model_name": "claude-3-5-haiku-20241022", "stop_reason": "tool_use", "stop_sequence": null }, "invalid_tool_calls": [] } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 43, "source": "loop", "writes": { "tools": { "messages": [ { "id": [ "langchain", "schema", "messages", "ToolMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "9aacf1e0-a50a-4ba7-ae61-634f1303d331", "name": "fetch_my_memories", "type": "tool", "status": "success", "content": "My name is Rachna. I live in delhi. When i travel to and from gurugram, my preferred mode of transport is metro between the times of 9am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car. When i travel to and from noida, my preferred mode of transport is metro between the times of 10am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car.", "tool_call_id": "toolu_01GbXo3cU51iXdc1cCj9LzNt" } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 44, "source": "loop", "writes": { "call_model": { "messages": [ { "id": [ "langchain", "schema", "messages", "AIMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "run-6087f78c-957e-40d0-8d9d-b2e93aeee6db-0", "type": "ai", "content": "Comprehensive Travel Plan:\n\nScheduled Meeting:\n- Date: June 18, 2025\n- Time: 11:00 AM\n- Location: Not specified\n\nTravel Recommendations:\n1. Transportation Preference:\n - Morning (9-11 AM): Prefer Metro\n - Evening (6-8 PM): Prefer Metro\n - Other times: Prefer personal car\n\nTravel Preparation:\n- Since the meeting is at 11:00 AM, this falls within your preferred metro time\n- Recommended transportation: Metro\n- Suggested departure time: 10:00 AM (allowing buffer time)\n\nTravel Checklist:\n1. Check metro route to meeting location\n2. Prepare metro card/fare\n3. Plan route and estimated travel time\n4. Backup transportation (personal car) if metro is inconvenient\n\nAdditional Recommendations:\n- Confirm exact meeting location to finalize travel details\n- Check metro schedule and potential alternate routes\n- Prepare necessary documents/materials for the meeting\n\nLimitations and Next Steps:\n- Meeting location is not specified\n- Only one meeting is currently scheduled\n- Recommend confirming more details about the meeting\n\nWould you like me to help you gather more specific information about the meeting location or transportation details?", "tool_calls": [], "usage_metadata": { "input_tokens": 2787, "total_tokens": 3068, "output_tokens": 281, "input_token_details": { "cache_read": 0, "cache_creation": 0 } }, "response_metadata": { "id": "msg_019FGguc7h4umcNuE5R3BCGe", "model": "claude-3-5-haiku-20241022", "usage": { "input_tokens": 2787, "service_tier": "standard", "output_tokens": 281, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0 }, "model_name": "claude-3-5-haiku-20241022", "stop_reason": "end_turn", "stop_sequence": null }, "invalid_tool_calls": [] } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ```
Author
Owner

@foie0222 commented on GitHub (Jun 17, 2025):

こんにちは@foie0222- 別のthread_idでテストしたら、うまくいきました! 根本的な原因を詳しく説明していただき、ありがとうございます。

もう1つだけ説明が必要です。

画像を添付します。表にあるように、各チェックポイントはthread_idに基づいています。user_id/checkpoint_nsは含まれていません。

したがって、このクエリでは何も削除されません。

DELETE FROM checkpoints 
WHERE thread_id = '6' AND checkpoint_ns = 'rachna1';

チェックポイント テーブルの checkpoint_ns 列に 'rachna1' という値があるはずですか?

Image

Apologies for the Confusion - Corrected Response 🙏
You're absolutely right to question this, and I apologize for the confusing explanation about checkpoint_ns.

I incorrectly suggested:

DELETE FROM checkpoints 
WHERE thread_id = '6' AND checkpoint_ns = 'rachna1';

This was wrong.

thread_id and user_id are completely different things:

  • thread_id = "6": Identifies this specific conversation

    • Stored in: checkpoints table
    • Purpose: Manages conversation state and flow
  • user_id = "rachna1": Identifies the user

    • Used for: Store/memory functionality (store.search, store.put)
    • Purpose: User-specific data across ALL conversations

The Correct Cleanup Query

As you correctly identified, this is all that's needed:

DELETE FROM checkpoints WHERE thread_id = '6';

Sorry for the confusion.

@foie0222 commented on GitHub (Jun 17, 2025): > こんにちは[@foie0222](https://github.com/foie0222)- 別のthread_idでテストしたら、**うまくいきました**! 根本的な原因を詳しく説明していただき、ありがとうございます。 > > もう1つだけ説明が必要です。 > > 画像を添付します。表にあるように、各チェックポイントはthread_idに基づいています。user_id/checkpoint_nsは含まれていません。 > > したがって、このクエリでは何も削除されません。 > > ``` > DELETE FROM checkpoints > WHERE thread_id = '6' AND checkpoint_ns = 'rachna1'; > ``` > > チェックポイント テーブルの checkpoint_ns 列に 'rachna1' という値があるはずですか? > > ![Image](https://github.com/user-attachments/assets/568d0caa-7ecf-4a6f-81f6-570b4817abc3) Apologies for the Confusion - Corrected Response 🙏 You're absolutely right to question this, and I apologize for the confusing explanation about `checkpoint_ns`. ❌I incorrectly suggested: ```sql DELETE FROM checkpoints WHERE thread_id = '6' AND checkpoint_ns = 'rachna1'; ``` This was wrong. **`thread_id` and `user_id` are completely different things:** - **`thread_id = "6"`**: Identifies **this specific conversation** - Stored in: `checkpoints` table - Purpose: Manages conversation state and flow - **`user_id = "rachna1"`**: Identifies **the user** - Used for: Store/memory functionality (`store.search`, `store.put`) - Purpose: User-specific data across ALL conversations ### The Correct Cleanup Query As you correctly identified, this is all that's needed: ```sql DELETE FROM checkpoints WHERE thread_id = '6'; ``` Sorry for the confusion.
Author
Owner

@foie0222 commented on GitHub (Jun 17, 2025):

I wanted to highlight something else as well. In the code that runs successfully, i checked the checkpoints table and below are the rows that got printed. In none of these rows do i find a 'tool_result' block as mentioned in the error message that i was getting.

I think these are tool_result !

{
    "step": 41,
    "source": "loop",
    "writes": {
        "tools": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "ToolMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "23a11961-6515-445c-8a07-8842ad6c803b",
                        "name": "get_google_calendar_events",
                        "type": "tool",
                        "status": "success",
                        "content": "{'dateTime': '2025-06-18T11:00:00+05:30', 'timeZone': 'Asia/Kolkata'}: meeting",
                        "tool_call_id": "toolu_01QgmJaZ87icSKYqoKAVEfan"
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
{
    "step": 43,
    "source": "loop",
    "writes": {
        "tools": {
            "messages": [
                {
                    "id": [
                        "langchain",
                        "schema",
                        "messages",
                        "ToolMessage"
                    ],
                    "lc": 1,
                    "type": "constructor",
                    "kwargs": {
                        "id": "9aacf1e0-a50a-4ba7-ae61-634f1303d331",
                        "name": "fetch_my_memories",
                        "type": "tool",
                        "status": "success",
                        "content": "My name is Rachna. I live in delhi. When i travel to and from gurugram, my preferred mode of transport is metro between the times of 9am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car. When i travel to and from noida, my preferred mode of transport is metro between the times of 10am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car.",
                        "tool_call_id": "toolu_01GbXo3cU51iXdc1cCj9LzNt"
                    }
                }
            ]
        }
    },
    "parents": {},
    "user_id": "rachna1",
    "thread_id": "7"
}
@foie0222 commented on GitHub (Jun 17, 2025): > I wanted to highlight something else as well. In the code that runs successfully, i checked the checkpoints table and below are the rows that got printed. **In none of these rows do i find a 'tool_result' block as mentioned in the error message that i was getting.** I think these are `tool_result` ! ``` { "step": 41, "source": "loop", "writes": { "tools": { "messages": [ { "id": [ "langchain", "schema", "messages", "ToolMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "23a11961-6515-445c-8a07-8842ad6c803b", "name": "get_google_calendar_events", "type": "tool", "status": "success", "content": "{'dateTime': '2025-06-18T11:00:00+05:30', 'timeZone': 'Asia/Kolkata'}: meeting", "tool_call_id": "toolu_01QgmJaZ87icSKYqoKAVEfan" } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ``` ``` { "step": 43, "source": "loop", "writes": { "tools": { "messages": [ { "id": [ "langchain", "schema", "messages", "ToolMessage" ], "lc": 1, "type": "constructor", "kwargs": { "id": "9aacf1e0-a50a-4ba7-ae61-634f1303d331", "name": "fetch_my_memories", "type": "tool", "status": "success", "content": "My name is Rachna. I live in delhi. When i travel to and from gurugram, my preferred mode of transport is metro between the times of 9am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car. When i travel to and from noida, my preferred mode of transport is metro between the times of 10am to 11am, and 6pm to 8pm. Rest of the times i prefer to drive my own car.", "tool_call_id": "toolu_01GbXo3cU51iXdc1cCj9LzNt" } } ] } }, "parents": {}, "user_id": "rachna1", "thread_id": "7" } ```
Author
Owner

@viv01 commented on GitHub (Jun 17, 2025):

ok. thank you for your responses @foie0222 . This was very helpful in solving my queries.

I will close the ticket now.

@viv01 commented on GitHub (Jun 17, 2025): ok. thank you for your responses @foie0222 . This was very helpful in solving my queries. I will close the ticket now.
Author
Owner

@viv01 commented on GitHub (Jun 19, 2025):

Hi @foie0222 , i was just thinking about the last comment/discussion in this thread on checkpointers.

in the checkpointer table, it is only storing thread_id and not the checkpoint_ns(user_id).

in the scenario where my postgres DB is a permanent store for my application which lets say 100 users use. won't this be a problem ? becasue all users are using the same backend and the thread_id can be same.

i think we should also store checkpoint_ns as the user_id in the checkpointers table to better differentiate between sessions of different users.

what is your view on this ?

@viv01 commented on GitHub (Jun 19, 2025): Hi @foie0222 , i was just thinking about the last comment/discussion in this thread on checkpointers. in the checkpointer table, it is only storing thread_id and not the checkpoint_ns(user_id). in the scenario where my postgres DB is a permanent store for my application which lets say 100 users use. won't this be a problem ? becasue all users are using the same backend and the thread_id can be same. i think we should also store checkpoint_ns as the user_id in the checkpointers table to better differentiate between sessions of different users. what is your view on this ?
Author
Owner

@foie0222 commented on GitHub (Jun 20, 2025):

Thank you for raising this important question about multi-user scenarios with a shared PostgreSQL backend. You're absolutely correct to be concerned about potential thread_id collisions when 100+ users share the same database. I took this opportunity to investigate further.

The Core Issue

In a multi-user environment, using simple thread_id values like "1", "2", etc., will indeed cause collisions where different users' conversations overwrite each other's checkpoint data.

Recommended Solution: User-Prefixed Thread IDs

The best practice is to include user identification in the thread_id itself:

# Recommended pattern
config = {
    "configurable": {
        "thread_id": f"user_{user_id}_conversation_{conversation_id}",
        "user_id": user_id,  # Optional: also store in metadata
    }
}

Examples:

  • user_alice_conversation_001
  • user_bob_session_planning
  • user_charlie_thread_xyz123

Understanding checkpoint_ns vs User Separation

It's important to clarify the role of checkpoint_ns (checkpoint namespace):

checkpoint_ns is internally managed by LangGraph for hierarchical execution contexts, particularly subgraphs. As seen in the source code:

# From libs/langgraph/langgraph/pregel/__init__.py
task_ns = f"{task.name}{NS_END}{task.id}"

Source: https://github.com/langchain-ai/langgraph/blob/main/libs/langgraph/langgraph/pregel/__init__.py

This creates namespaces like "search_agent:task_123" or "data_processor:task_456|chart_generator:task_789" for nested subgraph executions.

Database Schema and Metadata Storage

The PostgreSQL checkpoint tables use a composite primary key:

-- From langgraph-checkpoint-postgres schema
CREATE TABLE checkpoints (
    thread_id TEXT NOT NULL,
    checkpoint_ns TEXT NOT NULL DEFAULT '',  -- LangGraph managed
    checkpoint_id TEXT NOT NULL,
    parent_checkpoint_id TEXT,
    type TEXT,
    checkpoint JSONB NOT NULL,
    metadata JSONB NOT NULL DEFAULT '{}',    -- User metadata stored here
    PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
);

Source: https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-postgres

User metadata (like user_id) is stored in the metadata JSONB column, allowing for flexible filtering:

# Filtering by user metadata
checkpoints = checkpointer.list(
    config=config,
    filter={"user_id": user_id}
)

Complete Multi-User Implementation Example

import uuid
from typing import Optional

class MultiUserThreadManager:
    @staticmethod
    def create_user_thread_config(
        user_id: str, 
        session_name: Optional[str] = None
    ) -> dict:
        """Create a thread config with guaranteed uniqueness per user."""
        
        if session_name:
            thread_id = f"user_{user_id}_session_{session_name}"
        else:
            # Use UUID for guaranteed uniqueness
            thread_id = f"user_{user_id}_{uuid.uuid4().hex[:8]}"
        
        return {
            "configurable": {
                "thread_id": thread_id,
                "user_id": user_id,  # Also store in metadata
            }
        }

# Usage
manager = MultiUserThreadManager()
alice_config = manager.create_user_thread_config("alice", "work_planning")
bob_config = manager.create_user_thread_config("bob")

# Results in completely isolated threads:
# alice_config: {"thread_id": "user_alice_session_work_planning", "user_id": "alice"}
# bob_config: {"thread_id": "user_bob_a1b2c3d4", "user_id": "bob"}

Key Takeaways

  1. thread_id is your primary isolation mechanism - always include user identification
  2. checkpoint_ns is framework-managed - used for subgraph execution contexts, not user separation
  3. Metadata provides additional filtering - stored in JSONB for flexible queries
  4. Database schema naturally supports multi-tenancy through the composite primary key structure

This approach ensures complete data isolation between users while leveraging LangGraph's built-in checkpoint management capabilities.

References:

@foie0222 commented on GitHub (Jun 20, 2025): Thank you for raising this important question about multi-user scenarios with a shared PostgreSQL backend. You're absolutely correct to be concerned about potential thread_id collisions when 100+ users share the same database. I took this opportunity to investigate further. ## The Core Issue In a multi-user environment, using simple thread_id values like "1", "2", etc., will indeed cause collisions where different users' conversations overwrite each other's checkpoint data. ## Recommended Solution: User-Prefixed Thread IDs The best practice is to include user identification in the thread_id itself: ```python # Recommended pattern config = { "configurable": { "thread_id": f"user_{user_id}_conversation_{conversation_id}", "user_id": user_id, # Optional: also store in metadata } } ``` **Examples:** - `user_alice_conversation_001` - `user_bob_session_planning` - `user_charlie_thread_xyz123` ## Understanding checkpoint_ns vs User Separation It's important to clarify the role of `checkpoint_ns` (checkpoint namespace): `checkpoint_ns` is internally managed by LangGraph for hierarchical execution contexts, particularly subgraphs. As seen in the source code: ```python # From libs/langgraph/langgraph/pregel/__init__.py task_ns = f"{task.name}{NS_END}{task.id}" ``` Source: https://github.com/langchain-ai/langgraph/blob/main/libs/langgraph/langgraph/pregel/__init__.py This creates namespaces like `"search_agent:task_123"` or `"data_processor:task_456|chart_generator:task_789"` for nested subgraph executions. ## Database Schema and Metadata Storage The PostgreSQL checkpoint tables use a composite primary key: ```sql -- From langgraph-checkpoint-postgres schema CREATE TABLE checkpoints ( thread_id TEXT NOT NULL, checkpoint_ns TEXT NOT NULL DEFAULT '', -- LangGraph managed checkpoint_id TEXT NOT NULL, parent_checkpoint_id TEXT, type TEXT, checkpoint JSONB NOT NULL, metadata JSONB NOT NULL DEFAULT '{}', -- User metadata stored here PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id) ); ``` Source: https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-postgres User metadata (like `user_id`) is stored in the metadata JSONB column, allowing for flexible filtering: ```python # Filtering by user metadata checkpoints = checkpointer.list( config=config, filter={"user_id": user_id} ) ``` ## Complete Multi-User Implementation Example ```python import uuid from typing import Optional class MultiUserThreadManager: @staticmethod def create_user_thread_config( user_id: str, session_name: Optional[str] = None ) -> dict: """Create a thread config with guaranteed uniqueness per user.""" if session_name: thread_id = f"user_{user_id}_session_{session_name}" else: # Use UUID for guaranteed uniqueness thread_id = f"user_{user_id}_{uuid.uuid4().hex[:8]}" return { "configurable": { "thread_id": thread_id, "user_id": user_id, # Also store in metadata } } # Usage manager = MultiUserThreadManager() alice_config = manager.create_user_thread_config("alice", "work_planning") bob_config = manager.create_user_thread_config("bob") # Results in completely isolated threads: # alice_config: {"thread_id": "user_alice_session_work_planning", "user_id": "alice"} # bob_config: {"thread_id": "user_bob_a1b2c3d4", "user_id": "bob"} ``` ## Key Takeaways 1. **thread_id is your primary isolation mechanism** - always include user identification 2. **checkpoint_ns is framework-managed** - used for subgraph execution contexts, not user separation 3. **Metadata provides additional filtering** - stored in JSONB for flexible queries 4. **Database schema naturally supports multi-tenancy** through the composite primary key structure This approach ensures complete data isolation between users while leveraging LangGraph's built-in checkpoint management capabilities. ### References: - https://langchain-ai.github.io/langgraph/ - https://github.com/langchain-ai/langgraph/tree/main/libs/checkpoint-postgres - https://github.com/langchain-ai/langgraph/blob/151f97b1200188f78a9df8daca810a72b687dcec/libs/langgraph/langgraph/pregel/__init__.py
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#724