An analyst whose model keeps calling tools ends the run with GraphRecursionError
Version
0.5.1 (source checkout at 35543d0), and the v0.5.2 branch at 9968bd8
How you run it
Python package (TradingAgentsGraph)
LLM provider and models
Any; reproduced offline with the test suite's ScriptedModel.
Ticker and analysis date
NVDA, 2026-01-09 (the end-to-end test's fixture)
What happened, and what you expected
An analyst's tool loop has no bound of its own. On v0.5.2, _tools_or_done (tradingagents/graph/setup.py) sends the analyst to tools whenever its last message carries tool calls, and tools always returns to agent; on 0.5.1 the same loop is _tools_or_clear (setup.py:43-47) with the tools → agent edge at setup.py:119. The loop ends when the model answers without tool calls, or when the analyst's graph reaches recursion_limit (max_recur_limit, 100 by default; on 0.5.1 the whole run shares one count). If a model keeps requesting tools instead of writing its report, GraphRecursionError propagates out of propagate() and the run ends with no decision (default config, checkpoint_enabled False). In the repro below on v0.5.2 that takes about 50 tool rounds per looping analyst; on 0.5.1 the same repro fails after 50 rounds of the Market Analyst, without the During task line.
Expected: an analyst that keeps calling tools is stopped after a bounded number of rounds, without a report (the debates already present a missing report as unavailable through report_or_absent), and the run completes.
Steps or code to reproduce
# In tests/test_graph_end_to_end.py, next to ScriptedModel and the `offline` fixture:
class AlwaysToolsModel(ScriptedModel):
"""Requests its first bound tool on every turn."""
def _generate(self, messages, stop=None, run_manager=None, **kwargs):
if not self.tools:
return super()._generate(messages, stop, run_manager, **kwargs)
self._count()
tool = self.tools[0]
props = tool.tool_call_schema.model_json_schema()["properties"]
call = {"name": tool.name, "id": f"call_{len(self.calls)}",
"args": {k: v for k, v in ARGS.items() if k in props}}
return ChatResult(generations=[ChatGeneration(message=AIMessage(content="", tool_calls=[call]))])
def test_repro(tmp_path, monkeypatch, offline):
_graph(tmp_path, monkeypatch, AlwaysToolsModel()).propagate("NVDA", TRADE_DATE)Error output
E langgraph.errors.GraphRecursionError: Recursion limit of 100 reached without hitting a stop condition. You can increase the limit by setting the `recursion_limit` config key.
E For troubleshooting, visit: https://docs.langchain.com/oss/python/langgraph/errors/GRAPH_RECURSION_LIMIT
E During task with name 'Fundamentals Analyst' and id '<uuid>'#65 and #239 show the same error from real runs (#239 with one provider and not with GLM-4.5); I can't tell whether their cause was this loop. #515 asks for a spending stop for the same kind of loop.
Source: TauricResearch/TradingAgents