I hit something worth flagging here, since it directly interacts with the ToolCallLimitMiddleware fix discussed above, and shows a real gap in where that protection actually applies.
Setup:
I have a create_deep_agent orchestrator with ToolCallLimitMiddleware(tool_name="sigma_search", run_limit=2, exit_behavior="continue") on the main agent, plus one explicitly defined custom subagent (research-agent) scoped to a separate web-search tool only, not sigma_search.
Expected behavior:
Once the main agent’s sigma_search limit is hit, any further attempt should get intercepted with the corrective ToolMessage and the model should stop trying.
What actually happened:
The limit worked correctly for the main agent’s own direct calls. But at a later point in the same run, the trace shows a call to sigma_search originating from "lc_agent_name": "general-purpose", "ls_agent_type": "subagent", this is deepagents’ own default built-in subagent, not the research-agent I explicitly defined. That default subagent apparently inherited sigma_search from the parent’s tool list, and inside its own execution, it fired three parallel sigma_search calls in a single turn, each with a badly degenerate query, the same phrase (“client IP address source IP address”) repeated roughly 15 times in one string. This happened at langgraph_step: 112, with a 41,148-token prompt at that point, real evidence of coherence breakdown under accumulated context, not just a normal repeat.
Why the limit didn’t catch it:
A subagent runs as its own separate subgraph. Per the customization docs, “There is no SubAgentMiddleware inside a subagent graph”, meaning middleware attached to the parent agent doesn’t automatically extend into a subgraph’s internal execution. The sigma_search limit is real and enforced for the main agent’s own calls, but a tool call made from inside a delegated subagent runs in a separate scope the parent’s middleware instance never sees.
Open questions for the community:
- Is there a way to prevent the model from delegating to the default general-purpose subagent at all, short of a prompt instruction (which is unreliable on its own, per everything else in this thread)? Something like an explicit flag to disable/omit the default subagent entirely?
- Is there a supported way to make a
ToolCallLimitMiddlewareinstance (or an equivalent) apply within a subagent’s own execution scope, not just the parent’s? - Has anyone else seen a model effectively route around a tool-call limit by delegating the same intent to a subagent instead of calling the tool directly? Whether or not this was “intentional” workaround-seeking by the model versus incidental, the practical effect is the same, the limit didn’t hold everywhere it needed to.
Happy to share the full trace if useful.