Graph not getting continued from the interupt #220

Closed
opened 2026-02-20 17:32:23 -05:00 by yindo · 4 comments
Owner

Originally created by @vigneshmj1997 on GitHub (Sep 1, 2024).

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangGraph/LangChain documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangGraph/LangChain rather than my code.
  • I am sure this is better as an issue rather than a GitHub discussion, since this is a LangGraph bug and not a design question.

Example Code

@cv_app.post("/")
async def create_cv_graph(request: InputResumeModel):
    """
    Initializes the CV creation process by starting the graph.

    Args:
        request (InputResumeModel): The input resume model.

    Returns:
        The result of the graph invocation.
    """
    # Generate a unique thread ID for this session
    thread_id = generate_random_string(length=20)
    logger.info(f"Generated thread ID: {thread_id}")

    # Prepare the input data for the graph
    input_data = {"cv": [json.loads(request.model_dump_json())]}
    config = {"configurable": {"thread_id": thread_id}}

    # Invoke the graph to start the CV creation process
    return_value = graph.stream(
        input=input_data,
        config=config,
        stream_mode="values"
    )

    
    return return_value

@cv_app.get("/image")
def get_image():
    get_graph()

@cv_app.post("/message")
def get_response(request: ChatModel):
    """
    Handles the continuation of the graph based on the current state.

    Args:
        request (ChatModel): Input request model.
    """
    logger.info(f"Request received for message API: {request}")
    
    thread_id = request.thread
    config = {"configurable": {"thread_id": thread_id}}
    return_value = None

    # Retrieve the current state
    current_state = graph.get_state(config=config)[1][0]
    message_input = {"complete_messages": [request.message]}
    
    # Determine the next action based on the current state
    if current_state == "edit_cv":
        return_value = graph.invoke(
            input=message_input,
            config=config,
            stream_mode="stream"
        )
        
    elif current_state == "get_details":
        message_input["get_details_messages"] = [request.message]
        return_value = graph.invoke(
            input=message_input,
            config=config,
            stream_mode="values"
        )
        

    return return_value


def builder():
    # Add nodes to the graph
    graph = StateGraph(InputGraphState)
    graph.add_node("create_cv", create_cv_node)
    graph.add_node("get_details", get_details)
    graph.add_node("render_cv", render_cv)
    graph.add_node("edit_cv", edit_cv_node)
    # graph.add_node("get_missing_in_resume", get_missing_values)

    # Add edges to the graph
    graph.add_edge(START, "create_cv")
    graph.add_conditional_edges(
        "create_cv", check_exception,{"END":END,"render_cv":"render_cv"}
    )
    graph.add_conditional_edges(
        "render_cv",
        check_render_cv,{"END":END,"edit_cv":"edit_cv"}
        )
    graph.add_conditional_edges(
        "edit_cv", check_missing_values,{"get_details":"get_details","END":END,"render_cv":"render_cv"})
    graph.add_conditional_edges(
        "get_details", check_details,{"edit_cv":"edit_cv","END":END}
    )
    with pool.connection() as conn:
        checkpointer = PostgresSaver(conn)
        #checkpointer = MemorySaver()
        return graph.compile(
            checkpointer=checkpointer, interrupt_before=["get_details","edit_cv"]
            )

Error Message and Stack Trace (if applicable)

No error message

Description

I have used postgres checkpoint
when i call the /message api the graph is not getting continued from "edit_cv" its rather going to the start of "create_cv" there any thing in am missing out here

System Info

These are the version of libraries i am using
langgraph==0.2.15
langgraph-checkpoint==1.0.8
langgraph-checkpoint-postgres==1.0.4

Originally created by @vigneshmj1997 on GitHub (Sep 1, 2024). ### Checked other resources - [X] I added a very descriptive title to this issue. - [X] I searched the [LangGraph](https://langchain-ai.github.io/langgraph/)/LangChain documentation with the integrated search. - [X] I used the GitHub search to find a similar question and didn't find it. - [X] I am sure that this is a bug in LangGraph/LangChain rather than my code. - [X] I am sure this is better as an issue [rather than a GitHub discussion](https://github.com/langchain-ai/langgraph/discussions/new/choose), since this is a LangGraph bug and not a design question. ### Example Code ```python @cv_app.post("/") async def create_cv_graph(request: InputResumeModel): """ Initializes the CV creation process by starting the graph. Args: request (InputResumeModel): The input resume model. Returns: The result of the graph invocation. """ # Generate a unique thread ID for this session thread_id = generate_random_string(length=20) logger.info(f"Generated thread ID: {thread_id}") # Prepare the input data for the graph input_data = {"cv": [json.loads(request.model_dump_json())]} config = {"configurable": {"thread_id": thread_id}} # Invoke the graph to start the CV creation process return_value = graph.stream( input=input_data, config=config, stream_mode="values" ) return return_value @cv_app.get("/image") def get_image(): get_graph() @cv_app.post("/message") def get_response(request: ChatModel): """ Handles the continuation of the graph based on the current state. Args: request (ChatModel): Input request model. """ logger.info(f"Request received for message API: {request}") thread_id = request.thread config = {"configurable": {"thread_id": thread_id}} return_value = None # Retrieve the current state current_state = graph.get_state(config=config)[1][0] message_input = {"complete_messages": [request.message]} # Determine the next action based on the current state if current_state == "edit_cv": return_value = graph.invoke( input=message_input, config=config, stream_mode="stream" ) elif current_state == "get_details": message_input["get_details_messages"] = [request.message] return_value = graph.invoke( input=message_input, config=config, stream_mode="values" ) return return_value def builder(): # Add nodes to the graph graph = StateGraph(InputGraphState) graph.add_node("create_cv", create_cv_node) graph.add_node("get_details", get_details) graph.add_node("render_cv", render_cv) graph.add_node("edit_cv", edit_cv_node) # graph.add_node("get_missing_in_resume", get_missing_values) # Add edges to the graph graph.add_edge(START, "create_cv") graph.add_conditional_edges( "create_cv", check_exception,{"END":END,"render_cv":"render_cv"} ) graph.add_conditional_edges( "render_cv", check_render_cv,{"END":END,"edit_cv":"edit_cv"} ) graph.add_conditional_edges( "edit_cv", check_missing_values,{"get_details":"get_details","END":END,"render_cv":"render_cv"}) graph.add_conditional_edges( "get_details", check_details,{"edit_cv":"edit_cv","END":END} ) with pool.connection() as conn: checkpointer = PostgresSaver(conn) #checkpointer = MemorySaver() return graph.compile( checkpointer=checkpointer, interrupt_before=["get_details","edit_cv"] ) ``` ### Error Message and Stack Trace (if applicable) ```shell No error message ``` ### Description I have used postgres checkpoint when i call the /message api the graph is not getting continued from "edit_cv" its rather going to the start of "create_cv" there any thing in am missing out here ### System Info These are the version of libraries i am using langgraph==0.2.15 langgraph-checkpoint==1.0.8 langgraph-checkpoint-postgres==1.0.4
yindo closed this issue 2026-02-20 17:32:26 -05:00
Author
Owner

@gbaian10 commented on GitHub (Sep 1, 2024):

Is the graph in your get_response generated from the builder function?

The way you've written it seems incorrect.
You included the return statement within the with block, but when it returns, it also exits the with scope.
This triggers the with block's exit mechanism, closing the database connection resources.
As a result, the graph you return can't use the database connection object assigned to its checkpointer variable.

@gbaian10 commented on GitHub (Sep 1, 2024): Is the `graph` in your `get_response` generated from the `builder` function? The way you've written it seems incorrect. You included the `return` statement within the `with` block, but when it returns, it also exits the `with` scope. This triggers the `with` block's exit mechanism, closing the database connection resources. As a result, the `graph` you return can't use the database connection object assigned to its `checkpointer` variable.
Author
Owner

@vigneshmj1997 commented on GitHub (Sep 1, 2024):

But when i try to check the state of the graph using

logger.info(f"Current {current_state}")
logger.info(f"Next state {graph.get_state(config=config).next}")

I am getting the current state dict of the graph
and the next state is mentioned as edit_cv
also, there is no error raised regarding db connection
So i believe the error is not related to DB connection

Unrelated to the bug

  • What's the best way to build the graph is not the above way
@vigneshmj1997 commented on GitHub (Sep 1, 2024): But when i try to check the state of the graph using ``` logger.info(f"Current {current_state}") logger.info(f"Next state {graph.get_state(config=config).next}") ``` I am getting the current state dict of the graph and the next state is mentioned as `edit_cv` also, there is no error raised regarding db connection So i believe the error is not related to DB connection Unrelated to the bug - What's the best way to build the graph is not the above way
Author
Owner

@gbaian10 commented on GitHub (Sep 1, 2024):

@vigneshmj1997

After closely reviewing your code, I found the issue.
When you want to resume execution from an interrupted graph, you need to set the input of invoke to None.
Any actions to update the state before resuming should be done through update_state().

You can obtain more information from this document.

Here is an example.

from typing import Annotated, TypedDict

from langchain_core.messages import AnyMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, add_messages


class State(TypedDict, total=False):
    messages: Annotated[list[AnyMessage], add_messages]


def foo(state: State) -> State:
    print("This is foo")
    return {"messages": "This is foo"}


def bar(state: State) -> State:
    print("This is bar")
    return {"messages": "This is bar"}


graph_builder = StateGraph(State)
graph_builder.add_node("foo", foo)
graph_builder.add_node("bar", bar)
graph_builder.set_entry_point("foo")
graph_builder.add_edge("foo", "bar")
graph_builder.set_finish_point("bar")
app = graph_builder.compile(checkpointer=MemorySaver(), interrupt_before=["bar"])

thread_config = {"configurable": {"thread_id": "1"}}
print(app.invoke({"messages": [("human", "Hello ")]}, thread_config))

print("=" * 10 + " interrupt " + "=" * 10)

# print(app.invoke({"messages": [("human", "World!")]}, thread_config))
print(app.invoke(None, thread_config))

image

@gbaian10 commented on GitHub (Sep 1, 2024): @vigneshmj1997 After closely reviewing your code, I found the issue. When you want to resume execution from an interrupted graph, you need to set the `input` of `invoke` to `None`. Any actions to update the state before resuming should be done through `update_state()`. You can obtain more information from this [document](https://langchain-ai.github.io/langgraph/tutorials/introduction/#part-5-manually-updating-the-state). Here is an example. ```py from typing import Annotated, TypedDict from langchain_core.messages import AnyMessage from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import StateGraph, add_messages class State(TypedDict, total=False): messages: Annotated[list[AnyMessage], add_messages] def foo(state: State) -> State: print("This is foo") return {"messages": "This is foo"} def bar(state: State) -> State: print("This is bar") return {"messages": "This is bar"} graph_builder = StateGraph(State) graph_builder.add_node("foo", foo) graph_builder.add_node("bar", bar) graph_builder.set_entry_point("foo") graph_builder.add_edge("foo", "bar") graph_builder.set_finish_point("bar") app = graph_builder.compile(checkpointer=MemorySaver(), interrupt_before=["bar"]) thread_config = {"configurable": {"thread_id": "1"}} print(app.invoke({"messages": [("human", "Hello ")]}, thread_config)) print("=" * 10 + " interrupt " + "=" * 10) # print(app.invoke({"messages": [("human", "World!")]}, thread_config)) print(app.invoke(None, thread_config)) ``` ![image](https://github.com/user-attachments/assets/5f36253f-5809-4e59-bc68-35ed23827b17)
Author
Owner

@vigneshmj1997 commented on GitHub (Sep 2, 2024):

The solution works perfectly. Thank you for the prompt response. I am closing the issue now

@vigneshmj1997 commented on GitHub (Sep 2, 2024): The solution works perfectly. Thank you for the prompt response. I am closing the issue now
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#220