Skip to content

fix(planner): add iteration limit to PlanController to prevent unbounded agent loops#36

Open
jihunkeom wants to merge 1 commit intocuga-project:mainfrom
jihunkeom:bugfix/agent-loop-step-limit
Open

fix(planner): add iteration limit to PlanController to prevent unbounded agent loops#36
jihunkeom wants to merge 1 commit intocuga-project:mainfrom
jihunkeom:bugfix/agent-loop-step-limit

Conversation

@jihunkeom
Copy link

@jihunkeom jihunkeom commented Feb 15, 2026

Bug Fix Pull Request

Related Issue

Fixes #21

Description

PlanController can enter an unbounded loop, executing 800+ steps when the LLM
never returns conclude_task=True. This adds an iteration limit to force
graceful termination.

Type of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix that would cause existing functionality to not work as expected)

Root Cause

PlanController's loop-return path had no iteration cap. If the LLM kept
generating new subtasks without concluding, the agent would cycle indefinitely,
consuming tokens and compute.

Solution

  • Add plan_controller_iteration_count field to AgentState (default 0)
  • Increment on each loop return in PlanControllerNode; reset to 0 on fresh
    entry from TaskDecompositionAgent
  • When count exceeds max_plan_iterations (configurable, default 15), route
    directly to FinalAnswerAgent with partial progress summary
  • Set recursion_limit=135 on SDK invoke and stream calls to match
    agent_loop.py and prevent LangGraph-level recursion errors

Testing

  • I have tested this fix locally
  • I have added tests that prove my fix works
  • All new and existing tests passed
  • I have verified the bug no longer occurs

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if needed

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Added safeguards to prevent unbounded planning loops by enforcing maximum iteration limits (default: 15) and recursion limits (default: 135)
    • System now gracefully concludes with partial progress when iteration limits are exceeded
  • Tests

    • Added comprehensive test coverage for planning iteration limit behavior

…ded agent loops

  Add plan_controller_iteration_count to AgentState and enforce max_plan_iterations
  (default 15) in PlanController. When exceeded, the agent is routed to FinalAnswerAgent
  with partial progress. Also set recursion_limit=135 on SDK invoke/stream calls.

  Refs: cuga-project#21

Signed-off-by: Ji Hun Keom <Jihun.Keom@ibm.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

📝 Walkthrough

Walkthrough

These changes implement two-level protection against unbounded agent loops: a default recursion_limit of 135 at the SDK level and a PlanController iteration counter (max_plan_iterations default 15) that forces early task conclusion when exceeded.

Changes

Cohort / File(s) Summary
State Management
src/cuga/backend/cuga_graph/state/agent_state.py
Added plan_controller_iteration_count: int field with default value 0 to track loop iterations.
PlanController Loop Protection
src/cuga/backend/cuga_graph/nodes/task_decomposition_planning/plan_controller.py
Resets iteration count on entry from TaskDecompositionAgent; increments and enforces max_plan_iterations limit on each loop return; forces transition to FinalAnswerAgent when exceeded.
Configuration
src/cuga/settings.toml
Added max_plan_iterations configuration key with default value 15.
SDK Recursion Limit
src/cuga/sdk.py
Added default recursion_limit of 135 to run_config in three paths (invoke, stream, and shared setup) when not explicitly provided.
Unit Tests
src/system_tests/unit/test_plan_controller_step_limit.py
Comprehensive test coverage for iteration limit enforcement: forced conclusion when exceeded, counter reset on entry, normal operation within limits, and LLM invocation behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A loop that spun without an end,
Now bounded loops we shall defend,
With counters watching iteration's flight,
At fifteen rounds, we set things right,
No more eight-hundred steps astray—
The rabbit breaks infinite loops today! 🎉

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding an iteration limit to PlanController to prevent unbounded loops, which directly addresses the PR's primary objective.
Linked Issues check ✅ Passed The PR successfully implements the coding requirements from issue #21: adds iteration counting/reset logic, enforces max_plan_iterations limit with early exit to FinalAnswerAgent, and sets recursion_limit=135 in SDK calls.
Out of Scope Changes check ✅ Passed All changes are within scope: plan_controller_iteration_count field, iteration enforcement logic, recursion_limit configuration, settings update, and comprehensive test coverage—all directly address the unbounded loop issue.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/system_tests/unit/test_plan_controller_step_limit.py (1)

85-125: Good reset verification.

Consider adding an assertion that the returned Command routes to "APIPlannerAgent" (the expected fast-path destination for a single API subtask) to also validate the routing, not just the counter reset. Non-blocking.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sami-marreed sami-marreed force-pushed the main branch 9 times, most recently from 83568da to 1b3bfb6 Compare February 28, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Agent Loop can exceed more than 800 steps

1 participant