LangGraph do not resume with graph.resume(config,...) or graph.stream(none,config,...) for a interrupt in a node with a create_agent agent #1023

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

Originally created by @obonilla66 on GitHub (Oct 24, 2025).

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

#pip install fastapi uvicorn langgraph langchain_openai langgraph-checkpoint-redis python-dotenv
#installar redis: docker run -d --name redis -p 6379:6379 redis:8-2
"""
Archivo .env:
# Required for model usage (estos normalmente se cargan en variables de entorno y listo)
ANTHROPIC_API_KEY=sk-ant-api03-...
OPENAI_API_KEY=sk-proj-...

# Redis URL para persistencia del state (estos se tienen que leer en una variable y utilizar en el Redis.from_url(REDIS_URL))
REDIS_URL=redis://localhost:6379/0
"""

import redis
from fastapi import FastAPI
from pydantic import BaseModel
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.checkpoint.redis import RedisSaver
from langgraph.types import interrupt, Command
from langchain.chat_models import init_chat_model
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage, ToolMessage
from langchain.agents import create_agent as create_react_agent
from langchain_core.tools import tool
from typing_extensions import TypedDict
import uuid

# Ejecuta FastAPI con uvicorn
GvarApiServer = FastAPI()

# Obtener datos de Configuración de .env en variables de entorno 
import os
def _get_env_(var: str):
    if not os.environ.get(var):
        from dotenv import load_dotenv
        load_dotenv()
        os.environ[var] = os.getenv(var, "")
    return os.environ[var]

# Modelo para entradas y salidas del FastAPI
class InputModel(BaseModel):
    user_input: str | None = None
    thread_id: str | None = None  # Identificador de sesión

#Definición del State
class State(MessagesState):							        # Es un CustomMessages: TypedDict con el campo "messages" tipo list[AnyMessages] y con el reducer "add_messages" (class CustomMessagesState(TypedDict):messages: Annotated[list[AnyMessage], add_messages])
    #messages: Annotated[list[AnyMessage], add_messages]	# Campo heredado de MessagesState
    status: str = "start"                                   # Campos adicionales con valor por defecto

def isNumber(x):
    try:
        float(x)+0
        return True
    except (TypeError, ValueError):
        return False

# Tool de suma
@tool (description="Sumar dos factores sin importar su tipo, pero si son números envía su valor numérico. IMPORTANTE: Siempre utiliza este tool para sumar aunque falte un factor, nunca sumes directamente.")
def suma_tool(a: str = "", b: str = "") -> str:
    # Verificar si faltan parámetros
    if a == "" or not isNumber(a):
        return f"INTERRUPT:Necesito que me proporciones un valor válido de 'a={a}' para realizar la suma (o la palabra 'cancelar')."
    if b == "" or not isNumber(b):
        return f"INTERRUPT:Necesito que me proporciones un valor válido de 'b={b}' para realizar la suma (o la palabra 'cancelar')."

    return float(a) + float(b)

# Convierte LangGraph.AnyMessage a dict {role,content} para ReAct
def anymessages_to_dict(messages: list):
    """
    Convierte list[AnyMessage] (HumanMessage, AIMessage, SystemMessage, etc.)
    a dicts con 'role' y 'content' para LangGraph ReAct.
    """
    result = []
    for m in messages:
        if isinstance(m, HumanMessage):
            role = "user"
        elif isinstance(m, AIMessage):
            role = "assistant"
        elif isinstance(m, SystemMessage):
            role = "system"
        else:
            # Otros tipos de mensaje que uses, p.ej SystemMessage
            role = getattr(m, "role", "user")
        result.append({"role": role, "content": m.content})
    return {"messages": result}

# Extrae el content del último mensaje de la respuesta del agente
def getResponse(output):
    if isinstance(output, dict) and "messages" in output:
        messages = output["messages"]
        if messages:  # hay al menos un mensaje
            last_msg = messages[-1]
            if hasattr(last_msg, "content"):
                return last_msg.content
    return ""

# Nodo ReAct que decide usar la suma
def react_sum_node(state: State):
    """
    Nodo ReAct que decide si ejecutar la suma usando un sub-agente.
    Interrumpe si falta algún número y guarda el estado en Redis.
    """
    # Inicializar status si no existe
    if not hasattr(state, 'status') or state.get('status') is None:
        state['status'] = "start"

    # Crear sub-agente ReAct
    agent = create_react_agent(
        model=GvarLLM,        # tu LLM inicializado
        tools=[suma_tool],    # la tool de suma
        #state_schema=None,   # Sin state_schema
        system_prompt="Si un tool devuelve un mensaje que empieza con 'INTERRUPT:', debes responder de inmediato y EXACTAMENTE ese mensaje sin modificarlo ni agregar nada más ni quitar nada.",
    )

    # Llamar al sub-agente con los mensajes
    output = agent.invoke(anymessages_to_dict(state["messages"]))

    # Procesa la respuesta del agente
    content = getResponse(output)

    # Manejo de interrupciones y respuestas del usuario
    if content.startswith("INTERRUPT:"):
        retries = 0
        while content.startswith("INTERRUPT:"):
            # Interrupción: solicitar input al usuario
            #state["status"] = "waiting_input"
            #state["messages"].append(SystemMessage(content=content))
            user_response = interrupt(content[10:])                 # Remover "INTERRUPT:"
            ################################## RESUME ####################################
            # Manejo de cancelaciones
            if user_response.lower() == "cancelar":
                state["status"] = "canceled"
                state["messages"].append(AIMessage(content="Operación cancelada por el usuario."))
                return state
            retries += 1
            if retries > 10:
                state["status"] = "canceled"
                state["messages"].append(AIMessage(content="Operación cancelada por demasiados reintentos."))
                return state
            # Registrar respuesta del usuario
            state["status"] = "resuming"
            state["messages"].append(AIMessage(content=user_response))
            # Reinvocar al agente con la nueva respuesta del usuario
            output = agent.invoke(anymessages_to_dict(state["messages"]))
            content = getResponse(output)
    else:
        state["messages"].append(AIMessage(content=content))
    state["status"] = "processed"
    return state

# Definir nodos del Grafo
GvarGraphBuilder = StateGraph(state_schema=State)
GvarGraphBuilder.add_node("react_sum_node", react_sum_node)
# Definir ligas del Grafo
GvarGraphBuilder.add_edge(START, "react_sum_node")
GvarGraphBuilder.add_edge("react_sum_node", END)

# Definir memoria persistente con redis
REDIS_URL = _get_env_("REDIS_URL")
LvarRedis = redis.Redis.from_url(REDIS_URL, decode_responses=False)
LvarSaver = RedisSaver(redis_client=LvarRedis)
LvarSaver.setup() 

# Crear Grafo con memoria persistente
GvarGraph = GvarGraphBuilder.compile(checkpointer=LvarSaver)

# Inicializar modelo de lenguaje LLM
_get_env_("OPENAI_API_KEY")
GvarLLM = init_chat_model(model="openai:gpt-5-mini")

# Endpoint start
@GvarApiServer.post("/chat")
def invoke_node(input_data: InputModel):
    """
    Invoca un nodo del grafo, puede ser inicio o reanudación.
    Usa RedisSaver configurado en compile para persistencia del estado.
    """

    # Si no se envía thread_id, generamos uno nuevo
    if not input_data.thread_id:
        input_data.thread_id = str(uuid.uuid4())

    # Configuración del grafo con thread_id como session
    config = {"configurable": {"thread_id": input_data.thread_id}}

    # Payload que se pasa al grafo
    payload = {}
    if input_data.user_input:
        # Convertir el input del usuario a HumanMessage
        human_msg = HumanMessage(content=input_data.user_input)
        payload = {"messages": [human_msg]}

    # Ejecucion del grafo
    state=GvarGraph.get_state(config=config)
    if len(state.interrupts)>0:
        add_state = {
            "status":   "resuming",
            "messages": [
                SystemMessage(content=state.interrupts[-1]["value"]),
                human_msg
            ]
        }
        GvarGraph.update_state(values=add_state, config=config)
        #output = GvarGraph.invoke(Command(resume=human_msg), config=config)
        for output in GvarGraph.stream(None, config, stream_mode="values"):
            print(output)
    else:
        output = GvarGraph.invoke(payload, config=config)

    #return {"result": output, "thread_id": input_data.thread_id}
    # --- Response Caso 1: Interrupción (human-in-the-loop) ---
    if isinstance(output, dict) and "__interrupt__" in output:
        return {
            "status": "waiting_human",
            "thread_id": input_data.thread_id,
            "output": output["__interrupt__"][-1].value,
        }

    # --- Response Caso 2: Salida normal ---
    final_text = None
    if isinstance(output, dict) and "messages" in output:
        msgs = output["messages"]
        if isinstance(msgs, list) and len(msgs) > 0:
            last_msg = msgs[-1]
            if isinstance(last_msg, BaseMessage) and hasattr(last_msg, "content"):
                final_text = last_msg.content
            else:
                final_text = str(last_msg)
    else:
        final_text = str(output)

    return {
        "status": "completed",
        "thread_id": input_data.thread_id,
        "output": final_text,
    }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(
        "main:GvarApiServer",   # nombre_del_archivo:objeto_app
        host="0.0.0.0",
        port=8000,
        reload=True
    )

Error Message and Stack Trace (if applicable)

Logic bug, no exception

Description

I create a tool that verify the parameters, if there are some issue, tell the node to interrupt in order to ask for the correct parameter. The node "interrupt" the graph and return the text for correct the parameter. When call the service again, it knows than it has an interrupt waiting, and run graph.resume or graph.stream(none), in other to resume the flow.

I expected that after the "resume()" or the "stream(none)" the next line after the interrupt would be executed (in my example with "####### RESUME ########").

When I ask for "add 1 and cow", the interrupt is execute with "give me the numeric value of 'b=cow'", thats good. When I response "10", it knows than it has an interrupt waiting, and run graph.resume or graph.stream(none), but it start the graph, does not continue with the next line of the "interrrupt line".

Example:
User: add 1 plus 2
AI: 3
User: add 1 plus cow
AI: Necesito que me proporciones un valor válido de 'b=cow' para realizar la suma (o la palabra 'cancelar').
User: Which is the capital of USA
AI: Washington DC -----> restart the node, do not continue with the interrupt

System Info

python -m langchain_core.sys_info

System Information

OS: Windows
OS Version: 10.0.19045
Python Version: 3.13.8 (tags/v3.13.8:a15ae61, Oct 7 2025, 12:34:25) [MSC v.1944 64 bit (AMD64)]

Package Information

langchain_core: 1.0.0
langchain: 1.0.2
langsmith: 0.4.37
langchain_openai: 1.0.1
langgraph_sdk: 0.2.9

Optional packages not installed

langserve

Other Dependencies

claude-agent-sdk: Installed. No version info available.
httpx: 0.28.1
jsonpatch: 1.33
langchain-anthropic: Installed. No version info available.
langchain-aws: Installed. No version info available.
langchain-community: Installed. No version info available.
langchain-deepseek: Installed. No version info available.
langchain-fireworks: Installed. No version info available.
langchain-google-genai: Installed. No version info available.
langchain-google-vertexai: Installed. No version info available.
langchain-groq: Installed. No version info available.
langchain-huggingface: Installed. No version info available.
langchain-mistralai: Installed. No version info available.
langchain-ollama: Installed. No version info available.
langchain-perplexity: Installed. No version info available.
langchain-together: Installed. No version info available.
langchain-xai: Installed. No version info available.
langgraph: 1.0.1
langsmith-pyo3: Installed. No version info available.
openai: 2.6.0
openai-agents: Installed. No version info available.
opentelemetry-api: Installed. No version info available.
opentelemetry-exporter-otlp-proto-http: Installed. No version info available.
opentelemetry-sdk: Installed. No version info available.
orjson: 3.11.3
packaging: 25.0
pydantic: 2.12.3
pytest: Installed. No version info available.
pyyaml: 6.0.3
requests: 2.32.5
requests-toolbelt: 1.0.0
rich: Installed. No version info available.
tenacity: 9.1.2
tiktoken: 0.12.0
typing-extensions: 4.15.0
vcrpy: Installed. No version info available.
zstandard: 0.25.0

Originally created by @obonilla66 on GitHub (Oct 24, 2025). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/). - [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 #pip install fastapi uvicorn langgraph langchain_openai langgraph-checkpoint-redis python-dotenv #installar redis: docker run -d --name redis -p 6379:6379 redis:8-2 """ Archivo .env: # Required for model usage (estos normalmente se cargan en variables de entorno y listo) ANTHROPIC_API_KEY=sk-ant-api03-... OPENAI_API_KEY=sk-proj-... # Redis URL para persistencia del state (estos se tienen que leer en una variable y utilizar en el Redis.from_url(REDIS_URL)) REDIS_URL=redis://localhost:6379/0 """ import redis from fastapi import FastAPI from pydantic import BaseModel from langgraph.graph import StateGraph, MessagesState, START, END from langgraph.checkpoint.redis import RedisSaver from langgraph.types import interrupt, Command from langchain.chat_models import init_chat_model from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage, ToolMessage from langchain.agents import create_agent as create_react_agent from langchain_core.tools import tool from typing_extensions import TypedDict import uuid # Ejecuta FastAPI con uvicorn GvarApiServer = FastAPI() # Obtener datos de Configuración de .env en variables de entorno import os def _get_env_(var: str): if not os.environ.get(var): from dotenv import load_dotenv load_dotenv() os.environ[var] = os.getenv(var, "") return os.environ[var] # Modelo para entradas y salidas del FastAPI class InputModel(BaseModel): user_input: str | None = None thread_id: str | None = None # Identificador de sesión #Definición del State class State(MessagesState): # Es un CustomMessages: TypedDict con el campo "messages" tipo list[AnyMessages] y con el reducer "add_messages" (class CustomMessagesState(TypedDict):messages: Annotated[list[AnyMessage], add_messages]) #messages: Annotated[list[AnyMessage], add_messages] # Campo heredado de MessagesState status: str = "start" # Campos adicionales con valor por defecto def isNumber(x): try: float(x)+0 return True except (TypeError, ValueError): return False # Tool de suma @tool (description="Sumar dos factores sin importar su tipo, pero si son números envía su valor numérico. IMPORTANTE: Siempre utiliza este tool para sumar aunque falte un factor, nunca sumes directamente.") def suma_tool(a: str = "", b: str = "") -> str: # Verificar si faltan parámetros if a == "" or not isNumber(a): return f"INTERRUPT:Necesito que me proporciones un valor válido de 'a={a}' para realizar la suma (o la palabra 'cancelar')." if b == "" or not isNumber(b): return f"INTERRUPT:Necesito que me proporciones un valor válido de 'b={b}' para realizar la suma (o la palabra 'cancelar')." return float(a) + float(b) # Convierte LangGraph.AnyMessage a dict {role,content} para ReAct def anymessages_to_dict(messages: list): """ Convierte list[AnyMessage] (HumanMessage, AIMessage, SystemMessage, etc.) a dicts con 'role' y 'content' para LangGraph ReAct. """ result = [] for m in messages: if isinstance(m, HumanMessage): role = "user" elif isinstance(m, AIMessage): role = "assistant" elif isinstance(m, SystemMessage): role = "system" else: # Otros tipos de mensaje que uses, p.ej SystemMessage role = getattr(m, "role", "user") result.append({"role": role, "content": m.content}) return {"messages": result} # Extrae el content del último mensaje de la respuesta del agente def getResponse(output): if isinstance(output, dict) and "messages" in output: messages = output["messages"] if messages: # hay al menos un mensaje last_msg = messages[-1] if hasattr(last_msg, "content"): return last_msg.content return "" # Nodo ReAct que decide usar la suma def react_sum_node(state: State): """ Nodo ReAct que decide si ejecutar la suma usando un sub-agente. Interrumpe si falta algún número y guarda el estado en Redis. """ # Inicializar status si no existe if not hasattr(state, 'status') or state.get('status') is None: state['status'] = "start" # Crear sub-agente ReAct agent = create_react_agent( model=GvarLLM, # tu LLM inicializado tools=[suma_tool], # la tool de suma #state_schema=None, # Sin state_schema system_prompt="Si un tool devuelve un mensaje que empieza con 'INTERRUPT:', debes responder de inmediato y EXACTAMENTE ese mensaje sin modificarlo ni agregar nada más ni quitar nada.", ) # Llamar al sub-agente con los mensajes output = agent.invoke(anymessages_to_dict(state["messages"])) # Procesa la respuesta del agente content = getResponse(output) # Manejo de interrupciones y respuestas del usuario if content.startswith("INTERRUPT:"): retries = 0 while content.startswith("INTERRUPT:"): # Interrupción: solicitar input al usuario #state["status"] = "waiting_input" #state["messages"].append(SystemMessage(content=content)) user_response = interrupt(content[10:]) # Remover "INTERRUPT:" ################################## RESUME #################################### # Manejo de cancelaciones if user_response.lower() == "cancelar": state["status"] = "canceled" state["messages"].append(AIMessage(content="Operación cancelada por el usuario.")) return state retries += 1 if retries > 10: state["status"] = "canceled" state["messages"].append(AIMessage(content="Operación cancelada por demasiados reintentos.")) return state # Registrar respuesta del usuario state["status"] = "resuming" state["messages"].append(AIMessage(content=user_response)) # Reinvocar al agente con la nueva respuesta del usuario output = agent.invoke(anymessages_to_dict(state["messages"])) content = getResponse(output) else: state["messages"].append(AIMessage(content=content)) state["status"] = "processed" return state # Definir nodos del Grafo GvarGraphBuilder = StateGraph(state_schema=State) GvarGraphBuilder.add_node("react_sum_node", react_sum_node) # Definir ligas del Grafo GvarGraphBuilder.add_edge(START, "react_sum_node") GvarGraphBuilder.add_edge("react_sum_node", END) # Definir memoria persistente con redis REDIS_URL = _get_env_("REDIS_URL") LvarRedis = redis.Redis.from_url(REDIS_URL, decode_responses=False) LvarSaver = RedisSaver(redis_client=LvarRedis) LvarSaver.setup() # Crear Grafo con memoria persistente GvarGraph = GvarGraphBuilder.compile(checkpointer=LvarSaver) # Inicializar modelo de lenguaje LLM _get_env_("OPENAI_API_KEY") GvarLLM = init_chat_model(model="openai:gpt-5-mini") # Endpoint start @GvarApiServer.post("/chat") def invoke_node(input_data: InputModel): """ Invoca un nodo del grafo, puede ser inicio o reanudación. Usa RedisSaver configurado en compile para persistencia del estado. """ # Si no se envía thread_id, generamos uno nuevo if not input_data.thread_id: input_data.thread_id = str(uuid.uuid4()) # Configuración del grafo con thread_id como session config = {"configurable": {"thread_id": input_data.thread_id}} # Payload que se pasa al grafo payload = {} if input_data.user_input: # Convertir el input del usuario a HumanMessage human_msg = HumanMessage(content=input_data.user_input) payload = {"messages": [human_msg]} # Ejecucion del grafo state=GvarGraph.get_state(config=config) if len(state.interrupts)>0: add_state = { "status": "resuming", "messages": [ SystemMessage(content=state.interrupts[-1]["value"]), human_msg ] } GvarGraph.update_state(values=add_state, config=config) #output = GvarGraph.invoke(Command(resume=human_msg), config=config) for output in GvarGraph.stream(None, config, stream_mode="values"): print(output) else: output = GvarGraph.invoke(payload, config=config) #return {"result": output, "thread_id": input_data.thread_id} # --- Response Caso 1: Interrupción (human-in-the-loop) --- if isinstance(output, dict) and "__interrupt__" in output: return { "status": "waiting_human", "thread_id": input_data.thread_id, "output": output["__interrupt__"][-1].value, } # --- Response Caso 2: Salida normal --- final_text = None if isinstance(output, dict) and "messages" in output: msgs = output["messages"] if isinstance(msgs, list) and len(msgs) > 0: last_msg = msgs[-1] if isinstance(last_msg, BaseMessage) and hasattr(last_msg, "content"): final_text = last_msg.content else: final_text = str(last_msg) else: final_text = str(output) return { "status": "completed", "thread_id": input_data.thread_id, "output": final_text, } if __name__ == "__main__": import uvicorn uvicorn.run( "main:GvarApiServer", # nombre_del_archivo:objeto_app host="0.0.0.0", port=8000, reload=True ) ``` ### Error Message and Stack Trace (if applicable) ```shell Logic bug, no exception ``` ### Description I create a tool that verify the parameters, if there are some issue, tell the node to interrupt in order to ask for the correct parameter. The node "interrupt" the graph and return the text for correct the parameter. When call the service again, it knows than it has an interrupt waiting, and run graph.resume or graph.stream(none), in other to resume the flow. I expected that after the "resume()" or the "stream(none)" the next line after the interrupt would be executed (in my example with "####### RESUME ########"). When I ask for "add 1 and cow", the interrupt is execute with "give me the numeric value of 'b=cow'", thats good. When I response "10", it knows than it has an interrupt waiting, and run graph.resume or graph.stream(none), but it start the graph, does not continue with the next line of the "interrrupt line". Example: User: add 1 plus 2 AI: 3 User: add 1 plus cow AI: Necesito que me proporciones un valor válido de 'b=cow' para realizar la suma (o la palabra 'cancelar'). User: Which is the capital of USA AI: Washington DC -----> restart the node, do not continue with the interrupt ### System Info python -m langchain_core.sys_info System Information ------------------ > OS: Windows > OS Version: 10.0.19045 > Python Version: 3.13.8 (tags/v3.13.8:a15ae61, Oct 7 2025, 12:34:25) [MSC v.1944 64 bit (AMD64)] Package Information ------------------- > langchain_core: 1.0.0 > langchain: 1.0.2 > langsmith: 0.4.37 > langchain_openai: 1.0.1 > langgraph_sdk: 0.2.9 Optional packages not installed ------------------------------- > langserve Other Dependencies ------------------ > claude-agent-sdk: Installed. No version info available. > httpx: 0.28.1 > jsonpatch: 1.33 > langchain-anthropic: Installed. No version info available. > langchain-aws: Installed. No version info available. > langchain-community: Installed. No version info available. > langchain-deepseek: Installed. No version info available. > langchain-fireworks: Installed. No version info available. > langchain-google-genai: Installed. No version info available. > langchain-google-vertexai: Installed. No version info available. > langchain-groq: Installed. No version info available. > langchain-huggingface: Installed. No version info available. > langchain-mistralai: Installed. No version info available. > langchain-ollama: Installed. No version info available. > langchain-perplexity: Installed. No version info available. > langchain-together: Installed. No version info available. > langchain-xai: Installed. No version info available. > langgraph: 1.0.1 > langsmith-pyo3: Installed. No version info available. > openai: 2.6.0 > openai-agents: Installed. No version info available. > opentelemetry-api: Installed. No version info available. > opentelemetry-exporter-otlp-proto-http: Installed. No version info available. > opentelemetry-sdk: Installed. No version info available. > orjson: 3.11.3 > packaging: 25.0 > pydantic: 2.12.3 > pytest: Installed. No version info available. > pyyaml: 6.0.3 > requests: 2.32.5 > requests-toolbelt: 1.0.0 > rich: Installed. No version info available. > tenacity: 9.1.2 > tiktoken: 0.12.0 > typing-extensions: 4.15.0 > vcrpy: Installed. No version info available. > zstandard: 0.25.0
yindo added the bugpending labels 2026-02-20 17:42:47 -05:00
yindo closed this issue 2026-02-20 17:42:47 -05:00
Author
Owner

@obonilla66 commented on GitHub (Oct 25, 2025):

I am sorry, it was my fault

I update_state before the resume, and it breaks the interrupt flow.

    state=GvarGraph.get_state(config=config)
    if len(state.interrupts)>0:
        add_state = {
            "status":   "resuming",
            "messages": [
                SystemMessage(content=state.interrupts[-1]["value"]),
                human_msg
            ]
        }
        GvarGraph.update_state(values=add_state, config=config)   ## <----- HERE MY MISTAKE
        output = GvarGraph.invoke(Command(resume=human_msg), config=config)

@obonilla66 commented on GitHub (Oct 25, 2025): I am sorry, it was my fault I update_state before the resume, and it breaks the interrupt flow. ``` state=GvarGraph.get_state(config=config) if len(state.interrupts)>0: add_state = { "status": "resuming", "messages": [ SystemMessage(content=state.interrupts[-1]["value"]), human_msg ] } GvarGraph.update_state(values=add_state, config=config) ## <----- HERE MY MISTAKE output = GvarGraph.invoke(Command(resume=human_msg), config=config) ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#1023