-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Description
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
import functools
import operator
import asyncio
from typing import TypedDict, List, Callable, Annotated
from langgraph.errors import GraphInterrupt
from langgraph.graph import StateGraph, END
from langgraph.types import Command
def error_handler():
def decorator(func: Callable):
@functools.wraps(func)
async def async_wrapper(*args, **kwargs):
try:
print(f"Starting execution: {func.__name__}")
result = await func(*args, **kwargs)
print(f"Successfully completed: {func.__name__}")
return result
except GraphInterrupt:
raise
except Exception as e:
print(f"Error in {func.__name__}: {str(e)}")
# Attempt to redirect to exception node
return Command(goto="exception_node")
return async_wrapper
return decorator
class GraphState(TypedDict):
messages: Annotated[List[str], operator.add]
@error_handler()
async def node_a(state: GraphState):
print("---Executing Node A---")
raise ValueError("Test error from Node A") # This should trigger the jump
return {"messages": ["Message from Node A"]}
@error_handler()
async def node_b(state: GraphState):
print("---Executing Node B---")
return {"messages": ["Message from Node B"]}
async def exception_node(state: GraphState):
print("---Executing Exception_node---")
return {"messages": ["An error occurred"]}
# Build graph
workflow = StateGraph(GraphState)
workflow.add_node("A", node_a)
workflow.add_node("B", node_b)
workflow.add_node("exception_node", exception_node)
workflow.set_entry_point("A")
workflow.add_edge("A", "B") # Explicit edge
workflow.add_edge("B", END)
workflow.add_edge("exception_node", END)
app = workflow.compile()
async def main():
inputs = {"messages": []}
print("\n---Running---")
final_state = await app.ainvoke(inputs)
print("---Final State---")
print(final_state)
if __name__ == "__main__":
asyncio.run(main())Error Message and Stack Trace (if applicable)
Actual Output:
---Running---
Starting execution: node_a
---Executing Node A---
Error in node_a: Test error from Node A
Starting execution: node_b # UNEXPECTED: Node B should not run
---Executing Node B---
Successfully completed: node_b
---Executing Exception_node---
---Final State---
{'messages': ['Message from Node B', 'An error occurred']} # State merged from both paths
Expected Output:
---Running---
Starting execution: node_a
---Executing Node A---
Error in node_a: Test error from Node A
---Executing Exception_node--- # ONLY the exception node should run
---Final State---
{'messages': ['An error occurred']}Description
I implemented a unified error-handling decorator for all nodes in a StateGraph. The decorator catches exceptions and attempts to redirect the flow to a dedicated exception_node by returning a Command(goto="exception_node").
However, when a node throws an exception, the graph does not interrupt its pre-defined execution flow set by add_edge. Instead, it seems to execute both paths: the error handler triggers, but then the graph also proceeds to the next node specified by the original edge. This results in the exception_node and the subsequent normal node both being executed, which is contrary to the expected behavior where the graph should immediately jump to the exception handler and stop the normal flow.
Expected Behavior:
When a node’s error-handling decorator catches an exception and returns a Command(goto="exception_node"), I expect the graph to interrupt the current explicit edge flow and immediately transition to the specified exception_node. The subsequent node (as defined by add_edge) should not execute.
Actual Behavior:
The graph appears to not respect the interrupt from the decorator’s Command when explicit edges are defined. Both the exception_node (from the goto command) and the next node in the original chain (from add_edge) are invoked, leading to merged and incorrect state updates.
finally, we solve this issue by all use command, but it should be a good method, we can't change all our project graph to not use addadge, we should have the uniform process enhance for the whole graph node
System Info
OS: Darwin
OS Version: Darwin Kernel Version 24.6.0: Mon Aug 11 21:16:30 PDT 2025; root:xnu-11417.140.69.701.11~1/RELEASE_ARM64_T8132
Python Version: 3.12.11 (main, Dec 4 2025, 15:32:46) [Clang 17.0.0 (clang-1700.0.13.5)]