Skip to content

Add hard ceilings to max_iter and flow router hops to prevent unbounded loops#6370

Open
camgrimsec wants to merge 1 commit into
crewAIInc:mainfrom
camgrimsec:feat/upper-bound-guardrails
Open

Add hard ceilings to max_iter and flow router hops to prevent unbounded loops#6370
camgrimsec wants to merge 1 commit into
crewAIInc:mainfrom
camgrimsec:feat/upper-bound-guardrails

Conversation

@camgrimsec

@camgrimsec camgrimsec commented Jun 27, 2026

Copy link
Copy Markdown

Introduces two non-breaking guardrails identified in a security audit:

  • max_iter ceiling: enforces a hard upper bound of 500 via Pydantic gt=0, le=500 on the max_iter field across BaseAgent, BaseAgentExecutor, and the experimental AgentExecutor. The default remains 25. Prevents denial-of-wallet attacks where a misconfigured or malicious config (e.g. max_iter=10_000_000) would burn unbounded API credits.

  • Flow router hop limit: adds MAX_ROUTER_HOPS = 100 counter to the router loop in FlowRuntime._route(). Raises RuntimeError on cycle instead of hanging the event loop indefinitely.

Mitigates CWE-770 (unbounded resource consumption) and CWE-835 (loop with unreachable exit). Affects no well-formed workflows.

Summary by CodeRabbit

  • Bug Fixes
    • Added safety limits to agent iteration settings so excessively high values are rejected, helping prevent runaway usage and unexpected costs.
    • Added a cap on router-triggered loops to avoid infinite or overly long execution when flows are configured with circular routing.

Introduces two non-breaking guardrails identified in a security audit:

* max_iter ceiling: enforces a hard upper bound of 500 via Pydantic
  gt=0, le=500 on the max_iter field across BaseAgent, BaseAgentExecutor,
  and the experimental AgentExecutor. The default remains 25. Prevents
  denial-of-wallet attacks where a misconfigured or malicious config
  (e.g. max_iter=10_000_000) would burn unbounded API credits.

* Flow router hop limit: adds MAX_ROUTER_HOPS = 100 counter to the
  router loop in FlowRuntime._route(). Raises RuntimeError on cycle
  instead of hanging the event loop indefinitely.

Mitigates CWE-770 (unbounded resource consumption) and CWE-835 (loop
with unreachable exit). Affects no well-formed workflows.
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds upper-bound caps to prevent unbounded execution: MAX_ITER_CEILING = 500 is introduced and applied via Pydantic le= constraints on max_iter in BaseAgent, BaseAgentExecutor, and AgentExecutorState. A separate MAX_ROUTER_HOPS = 100 constant is added to the flow runtime, enforced by a new hop_count guard in _execute_listeners() that raises RuntimeError on circular router configurations.

Changes

Iteration and Router Hop Caps

Layer / File(s) Summary
Agent max_iter ceiling
lib/crewai/src/crewai/agents/agent_builder/base_agent.py, lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py, lib/crewai/src/crewai/experimental/agent_executor.py
Defines MAX_ITER_CEILING: Final[int] = 500 and applies it as le=MAX_ITER_CEILING (plus gt=0) on max_iter fields in all three agent classes.
Flow router hop cap and guard
lib/crewai/src/crewai/flow/runtime/__init__.py
Adds Final import and MAX_ROUTER_HOPS: Final[int] = 100, then updates _execute_listeners() to track hop_count and raise RuntimeError when exceeded.

Suggested Reviewers

  • greysonlalonde
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding hard ceilings for max_iter and router hops to prevent unbounded loops.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py (1)

27-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse MAX_ITER_CEILING instead of the literal 500.

The ceiling is now a named constant in base_agent.py, but this field (and AgentExecutor.max_iter) hard-code 500. For a security guardrail, three copies of the bound risk silent drift if it is ever changed in one place only. Consider importing and reusing the shared constant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py` at line
27, The max iteration ceiling is hard-coded here instead of using the shared
guardrail constant, which can drift from the value used elsewhere. Update the
max_iter Field definition in base_agent_executor and the AgentExecutor.max_iter
validation to import and reuse MAX_ITER_CEILING from base_agent.py so the bound
stays consistent in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai/src/crewai/flow/runtime/__init__.py`:
- Around line 2745-2753: The router hop cap in the flow runtime is too low for
valid experimental agent loops, so update the MAX_ROUTER_HOPS check in the
router loop to allow longer runs. Either raise the static limit or derive it
from the existing max_iter/max_method_calls settings used by the flow runtime so
the cycle guard stays consistent with those limits. Keep the change localized
around the hop_count validation and the router cycle detection logic.

---

Nitpick comments:
In `@lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py`:
- Line 27: The max iteration ceiling is hard-coded here instead of using the
shared guardrail constant, which can drift from the value used elsewhere. Update
the max_iter Field definition in base_agent_executor and the
AgentExecutor.max_iter validation to import and reuse MAX_ITER_CEILING from
base_agent.py so the bound stays consistent in one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bf047ac8-7c78-41d5-bc5a-1c45b6315c53

📥 Commits

Reviewing files that changed from the base of the PR and between 6491f5a and 0c1aa72.

📒 Files selected for processing (4)
  • lib/crewai/src/crewai/agents/agent_builder/base_agent.py
  • lib/crewai/src/crewai/agents/agent_builder/base_agent_executor.py
  • lib/crewai/src/crewai/experimental/agent_executor.py
  • lib/crewai/src/crewai/flow/runtime/__init__.py

Comment on lines +2745 to +2753
hop_count = 0

while True:
hop_count += 1
if hop_count > MAX_ROUTER_HOPS:
raise RuntimeError(
f"Flow router cycle detected: exceeded {MAX_ROUTER_HOPS} "
"router hops. Check for circular @router definitions."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Map the AgentExecutor router graph and confirm which routers can chain
# back into one another within a single _execute_listeners pass.
fd -t f 'agent_executor.py' lib/crewai/src/crewai/experimental \
  --exec rg -nP '`@router`\(|return "(initialized|continue_reasoning|todo_not_satisfied|check_iteration|parsed)"' {}

# Check for any existing test exercising many iterations through the flow router loop.
rg -nP --type=py -C2 'MAX_ROUTER_HOPS|max_iter\s*=\s*(1\d\d|[2-9]\d\d)' lib/crewai

Repository: crewAIInc/crewAI

Length of output: 2879


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the router functions involved in the AgentExecutor loop and the runtime hop counter.
sed -n '1320,1455p' lib/crewai/src/crewai/flow/runtime/__init__.py
printf '\n---\n'
sed -n '2050,2245p' lib/crewai/src/crewai/flow/runtime/__init__.py
printf '\n---\n'
sed -n '2660,2715p' lib/crewai/src/crewai/flow/runtime/__init__.py
printf '\n---\n'
sed -n '2735,2760p' lib/crewai/src/crewai/flow/runtime/__init__.py

Repository: crewAIInc/crewAI

Length of output: 18309


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the max-iteration configuration and the class that uses the router loop.
rg -n -C3 'max_iter|max_method_calls|MAX_ROUTER_HOPS|increment_and_continue|check_max_iterations|continue_reasoning|route_by_answer_type|check_todo_completion' lib/crewai/src/crewai/flow/runtime/__init__.py

Repository: crewAIInc/crewAI

Length of output: 1783


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the router bodies that form the suspected cycle.
sed -n '1338,1395p' lib/crewai/src/crewai/flow/runtime/__init__.py
printf '\n---\n'
sed -n '2088,2145p' lib/crewai/src/crewai/flow/runtime/__init__.py
printf '\n---\n'
sed -n '2234,2245p' lib/crewai/src/crewai/flow/runtime/__init__.py
printf '\n---\n'
sed -n '2648,2715p' lib/crewai/src/crewai/flow/runtime/__init__.py

Repository: crewAIInc/crewAI

Length of output: 8488


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for the agent-side max_iter default/configuration.
rg -n -C2 'max_iter\s*[:=]\s*500|max_iter\s*[:=]\s*\d+|max_iter' lib/crewai/src/crewai/agents lib/crewai/src/crewai/flow lib/crewai/src/crewai

Repository: crewAIInc/crewAI

Length of output: 25918


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the agent loop routers and the surrounding methods in the experimental executor.
sed -n '2080,2255p' lib/crewai/src/crewai/experimental/agent_executor.py
printf '\n---\n'
sed -n '2230,2305p' lib/crewai/src/crewai/experimental/agent_executor.py

Repository: crewAIInc/crewAI

Length of output: 10204


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show where the experimental executor re-enters listener execution so we can tell whether
# the router chain is bounded per method/result or across the whole agent run.
rg -n -C3 '_execute_listeners\(' lib/crewai/src/crewai/experimental/agent_executor.py

Repository: crewAIInc/crewAI

Length of output: 154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the missing router methods around the iteration/reasoning loop.
sed -n '1360,1465p' lib/crewai/src/crewai/experimental/agent_executor.py
printf '\n---\n'
sed -n '1465,1615p' lib/crewai/src/crewai/experimental/agent_executor.py
printf '\n---\n'
sed -n '1615,1775p' lib/crewai/src/crewai/experimental/agent_executor.py
printf '\n---\n'
sed -n '1775,1915p' lib/crewai/src/crewai/experimental/agent_executor.py

Repository: crewAIInc/crewAI

Length of output: 21862


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find every router/listener in the experimental executor that can feed back into the loop.
rg -n -C2 '`@router`\(|`@listen`\(' lib/crewai/src/crewai/experimental/agent_executor.py

Repository: crewAIInc/crewAI

Length of output: 5730


MAX_ROUTER_HOPS is too low for the experimental agent loop Consider raising it or deriving it from max_iter/max_method_calls; the current 100-hop cap can trip valid runs before they exhaust the 500-iteration limit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/flow/runtime/__init__.py` around lines 2745 - 2753, The
router hop cap in the flow runtime is too low for valid experimental agent
loops, so update the MAX_ROUTER_HOPS check in the router loop to allow longer
runs. Either raise the static limit or derive it from the existing
max_iter/max_method_calls settings used by the flow runtime so the cycle guard
stays consistent with those limits. Keep the change localized around the
hop_count validation and the router cycle detection logic.

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.

1 participant