Skip to content

[Bug]: Subagents hit max_iterations prematurely — shared IterationBudget drains parent's remaining quota #2873

@Mibayy

Description

@Mibayy

Bug Description

Subagents frequently exit with exit_reason: max_iterations before completing their task, even when max_iterations is set explicitly (e.g. 50). The issue is especially visible in batch mode (3 parallel subagents).

Root Cause

In tools/delegate_tool.py, line ~196:

shared_budget = getattr(parent_agent, "iteration_budget", None)
# ...
child = AIAgent(
    max_iterations=max_iterations,   # e.g. 50
    iteration_budget=shared_budget,  # <-- parent's already-partially-consumed budget
    ...
)

And in run_agent.py (AIAgent.init):

self.iteration_budget = iteration_budget or IterationBudget(max_iterations)

When a subagent is created, it receives the parent's shared IterationBudget instead of a fresh one. The parent has already consumed iterations during its own setup and reasoning. In batch mode with 3 parallel subagents, all 3 share the same depleted budget.

Example: Parent has max_iterations=90, has consumed 30 by the time it spawns 3 subagents with max_iterations=50. Each subagent thinks it has 50 iterations, but they all race to consume the remaining 60 — so each only gets ~20 on average before hitting the budget cap. The child's own max_iterations=50 is set but effectively overridden by the inherited budget.

Steps to Reproduce

  1. Run a task that requires ~20+ parent iterations before delegating
  2. Call delegate_task with tasks=[...] (3 parallel subagents), max_iterations=50
  3. Observe subagents finishing with exit_reason: max_iterations after only 15-25 tool calls, mid-task

Expected Behavior

A subagent created with max_iterations=50 should be allowed up to 50 LLM turns regardless of how many turns the parent has consumed. The budget cap on a subagent should be independent of the parent's consumption.

Proposed Fix

Give each subagent a fresh IterationBudget seeded from its own max_iterations, rather than inheriting the parent's shared (and partially consumed) budget:

# delegate_tool.py — _build_child_agent()

# Before (line ~196):
shared_budget = getattr(parent_agent, "iteration_budget", None)

# After:
# Each child gets its own budget capped at its own max_iterations.
# The parent budget is NOT shared — subagents should not be penalized
# for iterations the parent already consumed.
shared_budget = None  # let AIAgent.__init__ create a fresh IterationBudget(max_iterations)

This one-line change makes IterationBudget(max_iterations) in AIAgent.init always apply correctly for subagents.

Alternative / More Nuanced Fix

If the intent is to have a session-wide hard cap (parent + all children combined), that's a valid design — but it should be documented clearly and the max_iterations parameter on delegate_task should reflect the true remaining budget, not a misleading per-child number. Alternatively, expose a config flag like subagent_budget: isolated | shared.

Environment

  • hermes-agent (current main)
  • delegate_tool.py DEFAULT_MAX_ITERATIONS = 50
  • Reproducible with any task requiring 3 parallel subagents and a parent that has run 20+ turns first

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions