Skip to content

Remote Subgraph (using interrupt) not resuming #4879

Description

@tadangkhoa1999

Checked other resources

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

Example Code

#################
./demo_graph_1/agent.py
#################

from langchain_core.messages import AIMessage
from langgraph.graph import START, END, MessagesState, StateGraph
from langgraph.types import Command, interrupt


def node_a(state: MessagesState):
    return {"messages": [AIMessage("hello")]}


def node_b(state: MessagesState):
    feedback = interrupt("what is your name?")
    return Command(
        update={
            "messages": [
                AIMessage(f"hello {feedback}"),
            ],
        },
        goto="END",
    )


graph = StateGraph(MessagesState)
graph.add_node("node_a", node_a)
graph.add_node("node_b", node_b)
graph.add_edge(START, "node_a")
graph.add_edge("node_a", "node_b")
graph.add_edge("node_b", END)

agent = graph.compile()

#################
./demo_graph_1/langgraph.json
#################

{
  "dependencies": ["."],
  "graphs": {
    "agent-1": "./agent.py:agent"
  }
}

#################
./test_remote_graph_with_interrupt.py
#################

from uuid import uuid4
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.pregel.remote import RemoteGraph
from langgraph.types import Command
from langgraph.checkpoint.memory import MemorySaver


url = "http://127.0.0.1:2024/"
graph_name = "agent-1"
remote_graph = RemoteGraph(graph_name, url=url)
graph = StateGraph(MessagesState)
graph.add_node("child", remote_graph)
graph.add_edge(START, "child")

# from demo_graph_1.agent import agent
# graph = StateGraph(MessagesState)
# graph.add_node("child", agent)
# graph.add_edge(START, "child")

agent = graph.compile(checkpointer=MemorySaver())


thread_id = str(uuid4())
while True:
    run_id = uuid4()
    configurable = {"thread_id": thread_id}
    config = RunnableConfig(
        configurable=configurable,
        run_id=run_id,
    )
    human_message = input("Your input: ")
    print("\n---\n")

    state = agent.get_state(config=config)
    interrupted_tasks = [
        task for task in state.tasks if hasattr(task, "interrupts") and task.interrupts
    ]

    if interrupted_tasks:
        # assume user input is response to resume agent execution from interrupt
        human_input = Command(resume=human_message)
    else:
        human_input = {"messages": [HumanMessage(human_message)]}

    for stream_event in agent.stream(
        input=human_input, config=config, stream_mode=["updates", "messages"], subgraphs=True
    ):
        if not isinstance(stream_event, tuple):
            continue
        namespace, stream_mode, event = stream_event
        print(namespace, stream_mode, event)
        print("\n---\n")

Error Message and Stack Trace (if applicable)

Description

EXPECT: I designed a multi-agent system, in which a supervisor agent connects to many sub-agents (RemoteGraph). In which one sub-agent uses interrupt (human-in-the-loop).

BUG: However, when calling Command(resume="..."), the remote graph does not continue but gives interrupt commands.

Minimal reproducible example:

Terminal 1 - start remote graph

cd demo_graph_1
langgraph dev

Terminal 2 - test supervisor

python test_remote_graph_with_interrupt.py

Output:

Your input: hi

---

('child:1657878f-802d-3fe2-955f-7123f75dd83e', 'node_a:6e977d50-3d9b-6db4-b802-73ed39b1776d') messages [{'content': 'hello', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'ad79c6f1-66cb-494f-9226-a4b1836182bf', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}, {'created_by': 'system', 'thread_id': '1d789155-6ea6-49e1-8d8c-1776beb1239c', 'langgraph_step': 1, 'langgraph_node': 'node_a', 'langgraph_triggers': ['branch:to:node_a'], 'langgraph_path': ['__pregel_pull', 'node_a'], 'langgraph_checkpoint_ns': 'node_a:6e977d50-3d9b-6db4-b802-73ed39b1776d', 'run_attempt': 1, 'langgraph_version': '0.4.7', 'langgraph_plan': 'developer', 'langgraph_host': 'self-hosted', 'langgraph_api_url': 'http://127.0.0.1:2024', 'user-agent': 'langgraph-sdk-py/0.1.70', 'x-request-id': '388e9450-ed7b-4f71-be9c-28d0853057d8', 'langgraph_auth_user_id': '', 'langgraph_request_id': '388e9450-ed7b-4f71-be9c-28d0853057d8', 'run_id': '1f03d352-aa47-626e-b4a6-de293a569055', 'graph_id': 'agent-1', 'assistant_id': 'f665311d-50bf-50ad-bb32-f7d1e7a914f2', 'user_id': ''}]

---

('child:1657878f-802d-3fe2-955f-7123f75dd83e',) updates {'node_a': {'messages': [{'content': 'hello', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'ad79c6f1-66cb-494f-9226-a4b1836182bf', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}

---

('child:1657878f-802d-3fe2-955f-7123f75dd83e',) updates {'__interrupt__': [{'value': 'what is your name?', 'resumable': True, 'ns': ['node_b:c0795155-7e81-d98c-30be-dd4442117281'], 'when': 'during'}]}

---

() updates {'__interrupt__': (Interrupt(value='what is your name?', resumable=True, ns=['node_b:c0795155-7e81-d98c-30be-dd4442117281']),)}

---

Your input: khoa

---

('child:1657878f-802d-3fe2-955f-7123f75dd83e',) updates {'__interrupt__': [{'value': 'what is your name?', 'resumable': True, 'ns': ['node_b:c0795155-7e81-d98c-30be-dd4442117281'], 'when': 'during'}]}

---

() updates {'__interrupt__': (Interrupt(value='what is your name?', resumable=True, ns=['node_b:c0795155-7e81-d98c-30be-dd4442117281']),)}

---

Your input: khoa

---

('child:1657878f-802d-3fe2-955f-7123f75dd83e',) updates {'__interrupt__': [{'value': 'what is your name?', 'resumable': True, 'ns': ['node_b:c0795155-7e81-d98c-30be-dd4442117281'], 'when': 'during'}]}

---

() updates {'__interrupt__': (Interrupt(value='what is your name?', resumable=True, ns=['node_b:c0795155-7e81-d98c-30be-dd4442117281']),)}

---

Other experiments: When I don't use remote graph but use subgraph as usual, this error is no longer encountered

Change supervisor graph:

from demo_graph_1.agent import agent
graph = StateGraph(MessagesState)
graph.add_node("child", agent)
graph.add_edge(START, "child")

System Info

System Information

OS: Darwin
OS Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:26 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T8112
Python Version: 3.12.10 (main, Apr 8 2025, 11:35:47) [Clang 17.0.0 (clang-1700.0.13.3)]

Package Information

langchain_core: 0.3.63
langchain: 0.3.25
langchain_community: 0.3.24
langsmith: 0.3.43
langchain_openai: 0.3.18
langchain_text_splitters: 0.3.8
langgraph_api: 0.2.36
langgraph_cli: 0.2.10
langgraph_license: Installed. No version info available.
langgraph_runtime: Installed. No version info available.
langgraph_runtime_inmem: 0.2.0
langgraph_sdk: 0.1.70

Optional packages not installed

langserve

Other Dependencies

aiohttp<4.0.0,>=3.8.3: Installed. No version info available.
async-timeout<5.0.0,>=4.0.0;: Installed. No version info available.
blockbuster<2.0.0,>=1.5.24: Installed. No version info available.
click: 8.2.1
cloudpickle>=3.0.0: Installed. No version info available.
cryptography<45.0,>=42.0.0: Installed. No version info available.
dataclasses-json<0.7,>=0.5.7: Installed. No version info available.
httpx: 0.28.1
httpx-sse<1.0.0,>=0.4.0: Installed. No version info available.
httpx>=0.25.0: Installed. No version info available.
httpx>=0.25.2: Installed. No version info available.
jsonpatch<2.0,>=1.33: Installed. No version info available.
jsonschema-rs<0.30,>=0.20.0: Installed. No version info available.
langchain-anthropic;: Installed. No version info available.
langchain-aws;: Installed. No version info available.
langchain-azure-ai;: Installed. No version info available.
langchain-cohere;: Installed. No version info available.
langchain-community;: Installed. No version info available.
langchain-core<1.0.0,>=0.3.51: Installed. No version info available.
langchain-core<1.0.0,>=0.3.58: Installed. No version info available.
langchain-core<1.0.0,>=0.3.59: Installed. No version info available.
langchain-core<1.0.0,>=0.3.61: Installed. No version info available.
langchain-core>=0.2.38: 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-openai;: Installed. No version info available.
langchain-perplexity;: Installed. No version info available.
langchain-text-splitters<1.0.0,>=0.3.8: Installed. No version info available.
langchain-together;: Installed. No version info available.
langchain-xai;: Installed. No version info available.
langchain<1.0.0,>=0.3.25: Installed. No version info available.
langgraph-checkpoint>=2.0.23: Installed. No version info available.
langgraph-checkpoint>=2.0.25: Installed. No version info available.
langgraph-runtime-inmem<0.3,>=0.2.0: Installed. No version info available.
langgraph-sdk>=0.1.66: Installed. No version info available.
langgraph>=0.2: Installed. No version info available.
langgraph>=0.3.27: Installed. No version info available.
langsmith-pyo3: Installed. No version info available.
langsmith<0.4,>=0.1.125: Installed. No version info available.
langsmith<0.4,>=0.1.126: Installed. No version info available.
langsmith<0.4,>=0.1.17: Installed. No version info available.
langsmith>=0.1.112: Installed. No version info available.
numpy>=1.26.2;: Installed. No version info available.
numpy>=2.1.0;: Installed. No version info available.
openai-agents: Installed. No version info available.
openai<2.0.0,>=1.68.2: 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.10.18
orjson>=3.10.1: Installed. No version info available.
orjson>=3.9.7: Installed. No version info available.
packaging: 24.2
packaging<25,>=23.2: Installed. No version info available.
pydantic: 2.11.5
pydantic-settings<3.0.0,>=2.4.0: Installed. No version info available.
pydantic<3.0.0,>=2.7.4: Installed. No version info available.
pydantic>=2.7.4: Installed. No version info available.
pyjwt>=2.9.0: Installed. No version info available.
pytest: Installed. No version info available.
python-dotenv: 1.1.0
PyYAML>=5.3: Installed. No version info available.
requests: 2.32.3
requests-toolbelt: 1.0.0
requests<3,>=2: Installed. No version info available.
rich: Installed. No version info available.
SQLAlchemy<3,>=1.4: Installed. No version info available.
sse-starlette<2.2.0,>=2.1.0: Installed. No version info available.
sse-starlette>=2: Installed. No version info available.
starlette>=0.37: Installed. No version info available.
starlette>=0.38.6: Installed. No version info available.
structlog<26,>=24.1.0: Installed. No version info available.
structlog>23: Installed. No version info available.
tenacity!=8.4.0,<10,>=8.1.0: Installed. No version info available.
tenacity!=8.4.0,<10.0.0,>=8.1.0: Installed. No version info available.
tenacity>=8.0.0: Installed. No version info available.
tiktoken<1,>=0.7: Installed. No version info available.
truststore>=0.1: Installed. No version info available.
typing-extensions>=4.7: Installed. No version info available.
uvicorn>=0.26.0: Installed. No version info available.
watchfiles>=0.13: Installed. No version info available.
zstandard: 0.23.0

Activity

  1. tadangkhoa1999 commented on Jun 2, 2025

    @tadangkhoa1999
    Author

    Class RemoteGraph check if its input is Command Instance or not. However, if using it as a sub-graph, Input Command Instance is not sent to it.

    I think one way to handle this problem is: fix CompiledStateGraph.stream() and CompiledStateGraph.astream() functions with input as Command Instance.

    However I don't see the logic for handling Command Instance in the CompiledStateGraph class.

    Does someone have any suggestions?

  2. neda824 commented on Jul 1, 2026

    @neda824

    Confirming this on langgraph 1.2.6 / langgraph-sdk 0.4.2, in a real multi-deployment setup: a supervisor graph routes to two domain agents (an IT help desk and a dealer-support agent) that are deployed separately and reached via RemoteGraph nodes. The interrupt propagates up to the supervisor correctly, but Command(resume=...) on the supervisor thread does not propagate down to the remote thread — the remote starts a fresh run and re-asks its first question.

    Minimal repro (any deployed graph that calls interrupt())

    import uuid
    from langchain_core.messages import HumanMessage
    from langgraph.checkpoint.memory import InMemorySaver
    from langgraph.graph import StateGraph, START, END, MessagesState
    from langgraph.pregel.remote import RemoteGraph
    from langgraph.types import Command
    
    remote = RemoteGraph(
        "YOUR_GRAPH_ID",
        url="https://YOUR-DEPLOYMENT.us.langgraph.app",
        api_key="lsv2_...",
    )
    
    class ParentState(MessagesState, total=False):
        output_messages: list
    
    b = StateGraph(ParentState)
    b.add_node("remote", remote)          # RemoteGraph AS A NODE
    b.add_edge(START, "remote")
    b.add_edge("remote", END)
    g = b.compile(checkpointer=InMemorySaver())
    
    cfg = {"configurable": {"thread_id": str(uuid.uuid4())}}
    
    # TURN 1 — triggers an interrupt inside the remote graph
    g.invoke({"messages": [HumanMessage("<makes the remote ask a follow-up>")]}, cfg)
    st = g.get_state(cfg)
    print("turn 1 next:", st.next,
          "pending:", sum(len(t.interrupts or []) for t in st.tasks))
    
    # TURN 2 — resume the PARENT thread
    g.invoke(Command(resume="<answer to the remote's question>"), cfg)
    st = g.get_state(cfg)
    print("turn 2 next:", st.next,
          "pending:", sum(len(t.interrupts or []) for t in st.tasks))

    Observed:

    turn 1 next: ('remote',) pending: 1 # interrupt propagated UP ✓
    turn 2 next: ('remote',) pending: 1 # same interrupt re-asked ✗
    Expected: turn 2 forwards the resume to the remote thread, the remote completes, and the parent advances past the remote node — matching how a local in-process subgraph behaves in the identical graph.

    Root cause (matches tadangkhoa1999's comment)
    RemoteGraph.stream/astream already forwards a resume when the node's input is a Command (isinstance(input, Command) → serialize → command=). But on resume the node is not invoked with a Command: the resume value is delivered through the checkpoint's pending writes keyed by (task_id, RESUME) and exposed via the scratchpad (pregel/_algo.py), and interrupt() is the only consumer that reads it. A local subgraph resumes because it shares the checkpointer and replays its own nested checkpoint; a RemoteGraph node has no local checkpoint to replay and never reads the scratchpad, so the resume value sits in the parent's config, ignored.

    #7181 looks like the right fix — one gap to flag
    PR #7181's _get_resume_command() (translate the parent's pending RESUME write into Command(resume=...)) is exactly this bridge and resolves the single-remote-node case.

    One thing it doesn't cover: the remote thread_id is still derived from the bare parent thread_id (checkpoint_ns is in _CONF_DROPLIST). With two RemoteGraph nodes on one parent thread — which is the supervisor / multi-agent case — both map to the same remote thread_id and would interleave state. A complete fix probably needs to namespace the remote thread by parent thread_id + checkpoint_ns so each remote node keeps its own remote thread. Happy to help test or extend in that direction.

  3. IgorGanapolsky commented on Jul 23, 2026

    @IgorGanapolsky

    Reliability note

    Production automation failures usually reduce to a contract issue before a model/prompt issue:

    1. Success criteria — durable artifact id (message/booking/row), not only node green
    2. Side-effect before complete — never blind-retry without an idempotency key on the business object
    3. Tool vs path split — external action can succeed while the node/agent path still errors (silent-green / false-red)

    If you have one failing execution + expected vs actual, reply with stack/version and I will send a free root-cause checklist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingexternal

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions