Error: 'ToolNode' object is not iterable #127

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

Originally created by @zsitvat on GitHub (Jun 27, 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

tools = [retriever_tool, my_tool]

def agent(state):
    """
    Invokes the agent model to generate a response based on the current state. Given
    the question, it will decide to retrieve using the retriever tool, or simply end.

    Args:
        state (messages): The current state

    Returns:
        dict: The updated state with the agent response appended to messages
    """
    
    prompt = hub.pull("placeholder for the prompt")
    
    print("---CALL AGENT---")
    messages = state["messages"]
    model = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",streaming=True)
    model = model.bind_tools(tools=tools)
    agent = prompt | model
    response = agent.invoke({"question": messages[-1].content,"chat_history": str(messages[:-1]),"varos":"","context":""})
 
    return {"messages": [response]}

*****

from langgraph.graph import END, StateGraph
from langgraph.prebuilt import ToolNode

# Define a new graph
workflow = StateGraph(AgentState)

# Define the nodes we will cycle between
workflow.add_node("topic_validator",topic_validator)  # topic validator
workflow.add_node("router", chain)  # agent
workflow.add_node("agent", agent)  # agent

tools = ToolNode(tools=[retriever_tool, my_tool])
workflow.add_node("tools_node", tools) 

workflow.add_node("standalone", standalone)  # Re-writing the question

workflow.add_node("chains", chain)


workflow.set_entry_point("topic_validator")
workflow.add_edge("topic_validator", "router")
workflow.add_edge("router", "standalone")
workflow.add_edge("standalone", "agent")

workflow.add_conditional_edges(
    "agent",
    should_continue
)


workflow.add_edge("tools_node", "chains")
workflow.add_edge("chains", END)

# Compile
graph = workflow.compile(debug=True)

*****

inputs = {
    "messages": [
        ("user", "Question?"),
    ]
}

try:
    graph_result = graph.stream(inputs)
    for output in graph_result:
        print("OUTPUT:")
        for key, value in output.items():
            print(f"Output from node '{key}':")
            print(value)
        print("\n---\n")
except Exception as e:
    print(f"Error: {e}")

Error Message and Stack Trace (if applicable)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[163], line 1
----> 1 await graph.ainvoke(inputs)

File ~/.local/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1606, in Pregel.ainvoke(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug, **kwargs)
   1604 else:
   1605     chunks = []
-> 1606 async for chunk in self.astream(
   1607     input,
   1608     config,
   1609     stream_mode=stream_mode,
   1610     output_keys=output_keys,
   1611     input_keys=input_keys,
   1612     interrupt_before=interrupt_before,
   1613     interrupt_after=interrupt_after,
   1614     debug=debug,
   1615     **kwargs,
   1616 ):
   1617     if stream_mode == "values":
   1618         latest = chunk

File ~/.local/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1431, in Pregel.astream(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug)
   1428         del fut, task
   1430 # panic on failure or timeout
-> 1431 _panic_or_proceed(done, inflight, step)
   1432 # don't keep futures around in memory longer than needed
   1433 del done, inflight, futures

File ~/.local/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1639, in _panic_or_proceed(done, inflight, step)
   1637             inflight.pop().cancel()
   1638         # raise the exception
-> 1639         raise exc
   1641 if inflight:
   1642     # if we got here means we timed out
   1643     while inflight:
   1644         # cancel all pending tasks

File ~/.local/lib/python3.10/site-packages/langgraph/pregel/retry.py:120, in arun_with_retry(task, retry_policy, stream)
    118         pass
    119 else:
--> 120     await task.proc.ainvoke(task.input, task.config)
    121 # if successful, end
    122 break

File ~/.local/lib/python3.10/site-packages/langchain_core/runnables/base.py:2543, in RunnableSequence.ainvoke(self, input, config, **kwargs)
   2539 config = patch_config(
   2540     config, callbacks=run_manager.get_child(f"seq:step:{i+1}")
   2541 )
   2542 if i == 0:
-> 2543     input = await step.ainvoke(input, config, **kwargs)
   2544 else:
   2545     input = await step.ainvoke(input, config)

File ~/.local/lib/python3.10/site-packages/langgraph/utils.py:121, in RunnableCallable.ainvoke(self, input, config, **kwargs)
    117         ret = await asyncio.create_task(
    118             self.afunc(input, **kwargs), context=context
    119         )
    120     else:
--> 121         ret = await self.afunc(input, **kwargs)
    122 if isinstance(ret, Runnable) and self.recurse:
    123     return await ret.ainvoke(input, config)

File ~/.local/lib/python3.10/site-packages/langchain_core/runnables/config.py:557, in run_in_executor(executor_or_config, func, *args, **kwargs)
    553         raise RuntimeError from exc
    555 if executor_or_config is None or isinstance(executor_or_config, dict):
    556     # Use default executor with context copied from current context
--> 557     return await asyncio.get_running_loop().run_in_executor(
    558         None,
    559         cast(Callable[..., T], partial(copy_context().run, wrapper)),
    560     )
    562 return await asyncio.get_running_loop().run_in_executor(executor_or_config, wrapper)

File /usr/lib/python3.10/concurrent/futures/thread.py:58, in _WorkItem.run(self)
     55     return
     57 try:
---> 58     result = self.fn(*self.args, **self.kwargs)
     59 except BaseException as exc:
     60     self.future.set_exception(exc)

File ~/.local/lib/python3.10/site-packages/langchain_core/runnables/config.py:548, in run_in_executor.<locals>.wrapper()
    546 def wrapper() -> T:
    547     try:
--> 548         return func(*args, **kwargs)
    549     except StopIteration as exc:
    550         # StopIteration can't be set on an asyncio.Future
    551         # it raises a TypeError and leaves the Future pending forever
    552         # so we need to convert it to a RuntimeError
    553         raise RuntimeError from exc

Cell In[155], line 20
     18 messages = state["messages"]
     19 model = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",streaming=True)
---> 20 model = model.bind_tools(tools=tools)
     21 agent = prompt | model
     22 response = agent.invoke({"question": messages[-1].content,"chat_history": str(messages[:-1]),"varos":"","context":""})

File ~/.local/lib/python3.10/site-packages/langchain_openai/chat_models/base.py:892, in BaseChatOpenAI.bind_tools(self, tools, tool_choice, **kwargs)
    859 def bind_tools(
    860     self,
    861     tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
   (...)
    866     **kwargs: Any,
    867 ) -> Runnable[LanguageModelInput, BaseMessage]:
    868     """Bind tool-like objects to this chat model.
    869 
    870     Assumes model is compatible with OpenAI tool-calling API.
   (...)
    889             :class:`~langchain.runnable.Runnable` constructor.
    890     """
--> 892     formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
    893     if tool_choice:
    894         if isinstance(tool_choice, str):
    895             # tool_choice is a tool/function name

TypeError: 'ToolNode' object is not iterable

Description

I'm using langgraph with a tool user llm, but i get error Error: 'ToolNode' object is not iterable. I provided the part of the code.

System Info

%pip install -U langgraph langchain openai langchain_openai langchain_community

Originally created by @zsitvat on GitHub (Jun 27, 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 tools = [retriever_tool, my_tool] def agent(state): """ Invokes the agent model to generate a response based on the current state. Given the question, it will decide to retrieve using the retriever tool, or simply end. Args: state (messages): The current state Returns: dict: The updated state with the agent response appended to messages """ prompt = hub.pull("placeholder for the prompt") print("---CALL AGENT---") messages = state["messages"] model = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",streaming=True) model = model.bind_tools(tools=tools) agent = prompt | model response = agent.invoke({"question": messages[-1].content,"chat_history": str(messages[:-1]),"varos":"","context":""}) return {"messages": [response]} ***** from langgraph.graph import END, StateGraph from langgraph.prebuilt import ToolNode # Define a new graph workflow = StateGraph(AgentState) # Define the nodes we will cycle between workflow.add_node("topic_validator",topic_validator) # topic validator workflow.add_node("router", chain) # agent workflow.add_node("agent", agent) # agent tools = ToolNode(tools=[retriever_tool, my_tool]) workflow.add_node("tools_node", tools) workflow.add_node("standalone", standalone) # Re-writing the question workflow.add_node("chains", chain) workflow.set_entry_point("topic_validator") workflow.add_edge("topic_validator", "router") workflow.add_edge("router", "standalone") workflow.add_edge("standalone", "agent") workflow.add_conditional_edges( "agent", should_continue ) workflow.add_edge("tools_node", "chains") workflow.add_edge("chains", END) # Compile graph = workflow.compile(debug=True) ***** inputs = { "messages": [ ("user", "Question?"), ] } try: graph_result = graph.stream(inputs) for output in graph_result: print("OUTPUT:") for key, value in output.items(): print(f"Output from node '{key}':") print(value) print("\n---\n") except Exception as e: print(f"Error: {e}") ``` ### Error Message and Stack Trace (if applicable) ```shell --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[163], line 1 ----> 1 await graph.ainvoke(inputs) File ~/.local/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1606, in Pregel.ainvoke(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug, **kwargs) 1604 else: 1605 chunks = [] -> 1606 async for chunk in self.astream( 1607 input, 1608 config, 1609 stream_mode=stream_mode, 1610 output_keys=output_keys, 1611 input_keys=input_keys, 1612 interrupt_before=interrupt_before, 1613 interrupt_after=interrupt_after, 1614 debug=debug, 1615 **kwargs, 1616 ): 1617 if stream_mode == "values": 1618 latest = chunk File ~/.local/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1431, in Pregel.astream(self, input, config, stream_mode, output_keys, input_keys, interrupt_before, interrupt_after, debug) 1428 del fut, task 1430 # panic on failure or timeout -> 1431 _panic_or_proceed(done, inflight, step) 1432 # don't keep futures around in memory longer than needed 1433 del done, inflight, futures File ~/.local/lib/python3.10/site-packages/langgraph/pregel/__init__.py:1639, in _panic_or_proceed(done, inflight, step) 1637 inflight.pop().cancel() 1638 # raise the exception -> 1639 raise exc 1641 if inflight: 1642 # if we got here means we timed out 1643 while inflight: 1644 # cancel all pending tasks File ~/.local/lib/python3.10/site-packages/langgraph/pregel/retry.py:120, in arun_with_retry(task, retry_policy, stream) 118 pass 119 else: --> 120 await task.proc.ainvoke(task.input, task.config) 121 # if successful, end 122 break File ~/.local/lib/python3.10/site-packages/langchain_core/runnables/base.py:2543, in RunnableSequence.ainvoke(self, input, config, **kwargs) 2539 config = patch_config( 2540 config, callbacks=run_manager.get_child(f"seq:step:{i+1}") 2541 ) 2542 if i == 0: -> 2543 input = await step.ainvoke(input, config, **kwargs) 2544 else: 2545 input = await step.ainvoke(input, config) File ~/.local/lib/python3.10/site-packages/langgraph/utils.py:121, in RunnableCallable.ainvoke(self, input, config, **kwargs) 117 ret = await asyncio.create_task( 118 self.afunc(input, **kwargs), context=context 119 ) 120 else: --> 121 ret = await self.afunc(input, **kwargs) 122 if isinstance(ret, Runnable) and self.recurse: 123 return await ret.ainvoke(input, config) File ~/.local/lib/python3.10/site-packages/langchain_core/runnables/config.py:557, in run_in_executor(executor_or_config, func, *args, **kwargs) 553 raise RuntimeError from exc 555 if executor_or_config is None or isinstance(executor_or_config, dict): 556 # Use default executor with context copied from current context --> 557 return await asyncio.get_running_loop().run_in_executor( 558 None, 559 cast(Callable[..., T], partial(copy_context().run, wrapper)), 560 ) 562 return await asyncio.get_running_loop().run_in_executor(executor_or_config, wrapper) File /usr/lib/python3.10/concurrent/futures/thread.py:58, in _WorkItem.run(self) 55 return 57 try: ---> 58 result = self.fn(*self.args, **self.kwargs) 59 except BaseException as exc: 60 self.future.set_exception(exc) File ~/.local/lib/python3.10/site-packages/langchain_core/runnables/config.py:548, in run_in_executor.<locals>.wrapper() 546 def wrapper() -> T: 547 try: --> 548 return func(*args, **kwargs) 549 except StopIteration as exc: 550 # StopIteration can't be set on an asyncio.Future 551 # it raises a TypeError and leaves the Future pending forever 552 # so we need to convert it to a RuntimeError 553 raise RuntimeError from exc Cell In[155], line 20 18 messages = state["messages"] 19 model = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",streaming=True) ---> 20 model = model.bind_tools(tools=tools) 21 agent = prompt | model 22 response = agent.invoke({"question": messages[-1].content,"chat_history": str(messages[:-1]),"varos":"","context":""}) File ~/.local/lib/python3.10/site-packages/langchain_openai/chat_models/base.py:892, in BaseChatOpenAI.bind_tools(self, tools, tool_choice, **kwargs) 859 def bind_tools( 860 self, 861 tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], (...) 866 **kwargs: Any, 867 ) -> Runnable[LanguageModelInput, BaseMessage]: 868 """Bind tool-like objects to this chat model. 869 870 Assumes model is compatible with OpenAI tool-calling API. (...) 889 :class:`~langchain.runnable.Runnable` constructor. 890 """ --> 892 formatted_tools = [convert_to_openai_tool(tool) for tool in tools] 893 if tool_choice: 894 if isinstance(tool_choice, str): 895 # tool_choice is a tool/function name TypeError: 'ToolNode' object is not iterable ``` ### Description I'm using langgraph with a tool user llm, but i get error Error: 'ToolNode' object is not iterable. I provided the part of the code. ### System Info %pip install -U langgraph langchain openai langchain_openai langchain_community
yindo closed this issue 2026-02-20 17:28:01 -05:00
Author
Owner

@hwchase17 commented on GitHub (Jun 27, 2024):

you have two separate definintions of tools:

tools = [retriever_tool, my_tool]
...
tools = ToolNode(tools=[retriever_tool, my_tool])

and its trying to use one in a place where the other should be used

@hwchase17 commented on GitHub (Jun 27, 2024): you have two separate definintions of tools: ``` tools = [retriever_tool, my_tool] ... tools = ToolNode(tools=[retriever_tool, my_tool]) ``` and its trying to use one in a place where the other should be used
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#127