Hello everyone,
I have two sub-agents in my workflow.
Sub-Agent 1 consists of two nodes:
• Node 1 performs some processing and generates a set of questions that require user input.
• Node 2 is a create_react_agent node that performs further processing.
I am using interrupt_before=[Node 2] in the workflow.
Sub-Agent 2 works as expected.
I also have a Supervisor created using from langgraph_supervisor import create_supervisor, which invokes these sub-agents based on the requirement.
The issue I’m facing is related to state management. The entire workflow uses the same shared state. However, when the Supervisor invokes Sub-Agent 1 and it generates open questions (which should be stored in the state) and then interrupts, I’m unable to see those questions in the state.
Interestingly, when I run Sub-Agent 1 independently, the questions are correctly updated and visible in the state.
FYI, I’m using Checkpointer to store Agent State.
Could someone please help me understand why the state updates are not visible when Sub-Agent 1 is invoked through the Supervisor, and how to fix this?
Thanks in advance!
hi @DivyanshJain0001
could you share your code?
Here you go.
from langgraph_supervisor import create_supervisor
class MainAgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
input_content: str
identified_issues: str
open_questions: list[str]
remaining_steps: RemainingSteps
class AnalyzerOutput(BaseModel):
<................>
class ProcessorOutput(BaseModel):
<................>
```
```python
def content_analyzer(state: MainAgentState) -> Command:
"""
Node to analyze content to identify issues and suggest improvements.
Args:
.............
Returns:
...............
"""
prompt = ChatPromptTemplate.from_messages([
("system", PROMPT_2),
("human", PROMPT_1)])
chain = prompt | llm.with_structured_output(AnalyzerOutput)
analysis_result = chain.invoke({
"input_content": state["input_content"]
})
return Command(
update={
"identified_issues": f"Issues found and the recommendations are: {analysis_result.analysis}",
"open_questions": analysis_result.open_questions
}
)
```
```python
@tool(description=DESCRIPTION_2, parse_docstring=True)
def content_processor(input_content: str, action_plan: str, profiling_data: str) -> str:
"""
Tool to process content based on identified issues and recommended improvements.
Args:
....................
Returns:
....................
"""
prompt = ChatPromptTemplate.from_template("""You are an expert content processor. Your task is to improve the content by following best practices.
input_content: {input_content}
""")
chain = prompt | llm
analysis_result = chain.invoke({
"input_content": input_content
})
return analysis_result
```
```python
def processor_agent_node(state: MainAgentState) -> Command:
tools = [content_processor]
agent_executor = create_react_agent(
llm, tools, name="processor",
prompt=PROMPT_3,
response_format=ProcessorOutput,
checkpointer=checkpointer
)
response = agent_executor.invoke({"messages": state["messages"]}, config=config)
return Command(
update={
"processed_content": response["structured_response"].content,
"processing_report": response["structured_response"].report
}
)
```
```python
processor_workflow = StateGraph(MainAgentState)
processor_workflow.add_node("content_analyzer", content_analyzer)
processor_workflow.add_node("agent", processor_agent_node)
processor_workflow.add_edge(START, "content_analyzer")
processor_workflow.add_edge("content_analyzer", "agent")
processor_workflow.add_edge("agent", END)
processor_subagent = processor_workflow.compile(
checkpointer=checkpointer,
interrupt_before=["agent"],
name="content_processor_subagent"
)
```
## Sub Agent 2
```python
...................
```
## Sub Agent 3
```python
...................
```
## Supervisor Agent
```python
supervisor_agent_workflow = create_supervisor(
agents=[processor_subagent, subagent2, subagent3],
output_mode="full_history",
model=llm,
prompt=PROMPT_7,
state_schema=MainAgentState,
checkpointer=checkpointer,
return_intermediate_steps=True
)
```
```python
supervisor_agent = supervisor_agent_workflow.compile(name="Main_Agent", checkpointer=checkpointer)
```
When trying to print State at the end, I couldn’t find open_questions updated in the state. But when running processor_subagent standalone, I could see everything updated in the state as expected.
Hi @DivyanshJain0001
What’s actually happening with your state
You have:
processor_workflow = StateGraph(MainAgentState) with nodes:
content_analyzer → writes open_questions
agent → create_react_agent
- Compiled with
interrupt_before=["agent"]
- This compiled graph is then used as a sub-agent in a supervisor built by
create_supervisor(...).
So the execution looks like:
- Supervisor decides to run
processor_subagent.
- Inside
processor_subagent, content_analyzer runs and updates open_questions in the subgraph’s state.
- Before
agent runs, the static interrupt (interrupt_before=["agent"]) fires.
- Execution stops inside the subgraph. At this point:
- The subgraph’s state has
open_questions filled in.
- The parent (supervisor) graph’s state has not yet been updated with those changes.
This behavior is exactly how LangGraph treats nested graphs with interrupt_before.
How to actually see open_questions
Option A - Read the subgraph state (minimal changes)
If your only goal is “I want to inspect open_questions at the interrupt,” the most direct solution is:
- keep your current graph structure and
interrupt_before=["agent"],
- but when you hit the interrupt, query the state with subgraph state included.
Using LangGraph’s state API (or your checkpointer), that looks like:
config = {"configurable": {"thread_id": "some-thread-id"}}
# After the supervisor run pauses on the interrupt:
snapshot = supervisor_agent.get_state(config, subgraphs=True)
# `snapshot.values` -> supervisor-level state
# `snapshot.tasks` -> contains nested StateSnapshot for running subgraphs
# You can walk `snapshot.tasks` to find the task whose node is your
# `content_processor_subagent` and read its `state.values["open_questions"]`.
If you’re using a custom checkpointer directly, you can do the same thing by:
- using the same
thread_id you run the supervisor with, and
- looking at the child graph’s
checkpoint_ns / metadata to pull that subgraph snapshot.
Pros:
- No need to change your graph topology.
- Matches current LangGraph semantics and tests.
Cons:
- Slightly more involved: you have to traverse the nested state or read from the checkpointer, rather than just looking at “flat” state.
Option B - Don’t interrupt inside the subgraph
The reason you lose visibility at the supervisor level is that the interrupt is happening inside the nested graph. If you instead structure things so that the interrupt is at the top level, you avoid that problem entirely.
A common way to do this is:
-
Move content_analyzer out of processor_workflow and make it a top-level node (or part of the top-level graph that the supervisor controls).
-
Let content_analyzer write open_questions into MainAgentState at the top level.
-
Only after that node completes, call into the worker / create_react_agent part (with no inner interrupt_before).
In other words, instead of:
- outer graph → sub-agent (
content_analyzer → agent with inner interrupt)
you restructure to:
- outer graph:
- node A =
content_analyzer (top-level, no nesting)
- interrupt here if there are
open_questions
- node B = “processor agent” (the
create_react_agent-based node)
You can follow the “manual supervisor” pattern in the multi-agent docs to build this top-level graph yourself, rather than relying entirely on the prebuilt create_supervisor, so you have full control over where interrupts live and how state is shared.
Pros:
open_questions is just part of the top-level state; no need to dig into subgraphs.
- Easier to reason about if you have more fields you want to inspect at human breakpoints.
Cons:
- Requires some refactoring away from “all of Sub-Agent 1 behind a single node”.
Option C - Encode open_questions into messages
but I think it’s not worth trying right now, since it’s a bit dirty workaround imho.
Thanks for the detailed solution, Pawel. Really appreciate it.
For Option A, once I get the state using:
and can see the updated open_questions, how do I update the state with my answers and resume execution from where the agent was interrupted?