# NousResearch/hermes-agent · Subagent iteration budget isolation bug and fixes

## Open now

- Nothing open on GitHub.

A bug where subagents share the parent agent's iteration budget, causing premature max_iterations cutoffs. Multiple fix attempts address giving subagents independent, isolated iteration budgets.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #3004. Verify it addresses your case.
- **Recommended:** read PR #3004 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-3004`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.50`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

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

## Prior & competing PRs

- **#3004: fix(delegate): give subagents independent iteration budgets**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `hermes_cli/config.py`, `run_agent.py`, `tools/delegate_tool.py`
  - fix hunks:
  - `hermes_cli/config.py`

    ```diff
    @@ -317,6 +317,8 @@ def ensure_hermes_home():
             "provider": "",    # e.g. "openrouter" (empty = inherit parent provider + credentials)
             "base_url": "",    # direct OpenAI-compatible endpoint for subagents
             "api_key": "",     # API key for delegation.base_url (falls back to OPENAI_API_KEY)
    +        "max_iterations": 50,  # per-subagent iteration cap (each subagent gets its own budget,
    +                               # independent of the parent's max_iterations)
         },
     
         # Ephemeral prefill messages file — JSON list of {role, content} dicts
    ```
  - `run_agent.py`

    ```diff
    @@ -162,11 +162,15 @@ def _install_safe_stdio() -> None:
     
     
     class IterationBudget:
    -    """Thread-safe shared iteration counter for parent and child agents.
    +    """Thread-safe iteration counter for an agent.
     
    -    Tracks total LLM-call iterations consumed across a parent agent and all
    -    its subagents.  A single ``IterationBudget`` is created by the parent
    -    and passed to every child so they share the same cap.
    +    Each agent (parent or subagent) gets its own ``IterationBudget``.
    +    The parent's budget is capped at ``max_iterations`` (default 90).
    +    Each subagent gets an independent budget capped at
    +    ``delegation.max_iterations`` (default 50) — this means total
    +    iterations across parent + subagents can exceed the parent's cap.
    +    Users control the per-subagent limit via ``delegation.max_iterations``
    +    in config.yaml.
     
         ``execute_code`` (programmatic tool calling) iterations are refunded via
         :meth:`refund` so they don't eat into the budget.
    ```

    ```diff
    @@ -5719,7 +5723,7 @@ def run_conversation(
                 api_call_count += 1
                 if not self.iteration_budget.consume():
                     if not self.quiet_mode:
    -                    self._safe_print(f"\n⚠️  Session iteration budget exhausted ({self.iteration_budget.max_total} total across agent + subagents)")
    +                    self._safe_print(f"\n⚠️  Iteration budget exhausted ({self.iteration_budget.used}/{self.iteration_budget.max_total} iterations used)")
                     break
     
                 # Fire step_callback for gateway hooks (agent:step event)
    ```

    ```diff
    @@ -7178,7 +7182,7 @@ def _stop_spinner():
                 or self.iteration_budget.remaining <= 0
             ):
                 if self.iteration_budget.remaining <= 0 and not self.quiet_mode:
    -                print(f"\n⚠️  Session iteration budget exhausted ({self.iteration_budget.used}/{self.iteration_budget.max_total} used, including subagents)")
    +                print(f"\n⚠️  Iteration budget exhausted ({self.iteration_budget.used}/{self.iteration_budget.max_total} iterations used)")
                 final_response = self._handle_max_iterations(messages, api_call_count)
             
             # Determine if conversation completed successfully
    ```
  - `tools/delegate_tool.py`

    ```diff
    @@ -191,9 +191,10 @@ def _build_child_agent(
         # Build progress callback to relay tool calls to parent display
         child_progress_cb = _build_child_progress_callback(task_index, parent_agent)
     
    -    # Share the parent's iteration budget so subagent tool calls
    -    # count toward the session-wide limit.
    -    shared_budget = getattr(parent_agent, "iteration_budget", None)
    +    # Each subagent gets its own iteration budget capped at max_iterations
    +    # (configurable via delegation.max_iterations, default 50).  This means
    +    # total iterations across parent + subagents can exceed the parent's
    +    # max_iterations.  The user controls the per-subagent cap in config.yaml.
     
         # Resolve effective credentials: config override > parent inherit
         effective_model = model or parent_agent.model
    ```

    ```diff
    @@ -230,7 +231,7 @@ def _build_child_agent(
             providers_order=parent_agent.providers_order,
             provider_sort=parent_agent.provider_sort,
             tool_progress_callback=child_progress_cb,
    -        iteration_budget=shared_budget,
    +        iteration_budget=None,  # fresh budget per subagent
         )
         # Set delegation depth so children can't spawn grandchildren
         child._delegate_depth = getattr(parent_agent, '_delegate_depth', 0) + 1
    ```
- **#2875: fix(delegation): isolate subagent iteration budgets by default (fixes #2873)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#3004`
- **#2928: fix(delegate): give subagents isolated iteration budgets**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#3004`

## Fix status

- Merged to main in PR #3004 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-03-25`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `agent`
