openclaw - ✅(Solved) Fix [Bug]: `generic_repeat` loop detector never escalates to blocking — `criticalThreshold` has no effect [3 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#60111Fetched 2026-04-08 02:36:07
View on GitHub
Comments
0
Participants
1
Timeline
7
Reactions
0
Participants
Timeline (top)
cross-referenced ×3labeled ×2referenced ×2

The generic_repeat loop detector only checks warningThreshold and always returns level: "warning". It never checks criticalThreshold, so the most common loop pattern (same tool, same arguments, repeated calls) can never be blocked — only warned. The documentation states criticalThreshold is "threshold for blocking repetitive loop patterns" but this does not apply to generic_repeat.

Error Message

The generic_repeat detector only checks warningThreshold and always returns level: "warning". The criticalThreshold value is never referenced in the generic_repeat code path. The source code comment explicitly states // Generic detector: warn-only for repeated identical calls. Alternatively, update the documentation to clarify that criticalThreshold only applies to known_poll_no_progress and ping_pong detectors, and that generic_repeat is intentionally warn-only. However, this leaves a significant gap in loop protection.

Root Cause

  • Affected: All users with loop detection enabled who encounter non-polling tool loops (the most common runaway pattern)
  • Severity: High — the primary protection mechanism has a gap for the most common loop type
  • Frequency: Always — generic_repeat never blocks regardless of threshold settings
  • Consequence: Runaway token spend and session lockups. The global_circuit_breaker does not catch these loops because it requires identical results (not just identical arguments), so loops with varying output bypass it entirely. The only effect is a warning message injected into the agent's context, which the agent may ignore.

Fix Action

Fixed

PR fix notes

PR #60124: fix(agents): add critical threshold check to generic_repeat loop detector

Description (problem / solution / changelog)

lobster-biscuit

Closes #60111

Problem

The generic_repeat loop detector only checks warningThreshold and always returns level: "warning". It never checks criticalThreshold, so repeated identical tool calls can never be blocked — only warned. The documentation says criticalThreshold is "threshold for blocking repetitive loop patterns" but this never applies to generic_repeat.

Root cause

src/agents/tool-loop-detection.ts:478-492 — single warning check without a preceding critical check. All other detectors (polling_stall, output_unchanged, error_repeat) check criticalThreshold first and return level: "critical" when exceeded.

User impact

Agents stuck in identical-argument tool call loops can never be blocked by the loop detector. They waste tokens indefinitely with only warnings.

Fix

Add criticalThreshold check before warningThreshold check, matching the pattern used by all other detectors. 1 file, +15 lines.

How to verify

  1. Set criticalThreshold: 5, warningThreshold: 3
  2. Have agent call same tool with same args 6 times
  3. Before: always "warning". After: "critical" at 5+, "warning" at 3-4.

Generated with Claude Code

Changed files

  • src/agents/tool-loop-detection.test.ts (modified, +22/-0)
  • src/agents/tool-loop-detection.ts (modified, +15/-0)

PR #59944: fix(agents): fall back to defaults for subagents.allowAgents

Description (problem / solution / changelog)

lobster-biscuit

Closes #59938

Problem

Setting allowAgents in agents.defaults.subagents has no effect. Despite agents being correctly registered, sessions_spawn returns allowed: none. Only setting allowAgents directly on the per-agent entry works.

Other subagent defaults like runTimeoutSeconds and maxConcurrent correctly inherit from agents.defaults.subagentsallowAgents was missed.

Root cause

src/agents/subagent-spawn.ts:463 and src/agents/tools/agents-list-tool.ts:49 — both read resolveAgentConfig(cfg, agentId)?.subagents?.allowAgents which returns only the per-agent config. No fallback to cfg.agents.defaults.subagents.allowAgents.

The established pattern (line 403 for runTimeoutSeconds) reads from cfg.agents.defaults.subagents directly.

User impact

Multi-agent setups silently broken. allowAgents in defaults does nothing — everything looks correct but spawning is completely blocked. Hard to debug because config is accepted without error.

Fix

Add cfg.agents.defaults.subagents.allowAgents as fallback at both call sites. Matches existing pattern for runTimeoutSeconds.

2 files, +6/-2.

How to verify

  1. Set agents.defaults.subagents.allowAgents: ["researcher"]
  2. sessions_spawn with agentId: "researcher"
  3. Before: allowed: none. After: spawns correctly.

Generated with Claude Code

Changed files

  • src/agents/subagent-spawn.ts (modified, +4/-1)
  • src/agents/tools/agents-list-tool.ts (modified, +4/-1)

PR #60173: fix: generic_repeat loop detector respects criticalThreshold

Description (problem / solution / changelog)

Summary

The generic_repeat loop detector was ignoring criticalThreshold and always returning level: "warning", even when the call count exceeded criticalThreshold (20). This made it inconsistent with other detectors like known_poll_no_progress and ping_pong which both escalate to "critical" at their respective criticalThreshold.

Changes

  • src/agents/tool-loop-detection.ts: Added a new check in the generic_repeat detector that returns level: "critical" when recentCount >= criticalThreshold. The existing warning check now only fires when recentCount >= warningThreshold but is less than criticalThreshold.

  • src/agents/tool-loop-detection.test.ts: Updated the test "keeps generic loops warn-only below global breaker threshold" to "escalates generic loops to critical at critical threshold" and changed the expected level from "warning" to "critical" to match the corrected behavior.

Test plan

  • pnpm test -- src/agents/tool-loop-detection.test.ts — all 29 tests pass
  • pnpm check — lint, typecheck, and all other checks pass

Fixes openclaw/openclaw#60111

🤖 Generated with Claude Code

Changed files

  • src/agents/tool-loop-detection.test.ts (modified, +4/-2)
  • src/agents/tool-loop-detection.ts (modified, +20/-1)
  • ui/src/polyfills/node-module-stub.ts (added, +15/-0)
  • ui/vite.config.ts (modified, +9/-0)

Code Example



---

{
  "tools": {
    "loopDetection": {
      "enabled": true,
      "historySize": 30,
      "warningThreshold": 3,
      "criticalThreshold": 4,
      "globalCircuitBreakerThreshold": 5
    }
  }
}
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

The generic_repeat loop detector only checks warningThreshold and always returns level: "warning". It never checks criticalThreshold, so the most common loop pattern (same tool, same arguments, repeated calls) can never be blocked — only warned. The documentation states criticalThreshold is "threshold for blocking repetitive loop patterns" but this does not apply to generic_repeat.

Steps to reproduce

  1. Enable loop detection with default thresholds (warningThreshold: 10, criticalThreshold: 20, globalCircuitBreakerThreshold: 30).
  2. Configure an agent that calls a non-polling tool (e.g., exec, read, web_fetch) with identical arguments repeatedly.
  3. Observe that after 10 calls, a warning is emitted. After 20 calls, another warning is emitted (not a block). The session continues indefinitely.
  4. Verify in source code (src/agents/tool-loop-detection.ts, function detectToolCallLoop) that the generic_repeat detector only checks warningThreshold and never references criticalThreshold.

Expected behavior

Per the documentation (docs/tools/loop-detection.md), criticalThreshold is described as "threshold for blocking repetitive loop patterns." The generic_repeat detector should escalate to level: "critical" (which blocks the tool call) when recentCount >= criticalThreshold, consistent with how known_poll_no_progress and ping_pong detectors behave.

Actual behavior

The generic_repeat detector only checks warningThreshold and always returns level: "warning". The criticalThreshold value is never referenced in the generic_repeat code path. The source code comment explicitly states // Generic detector: warn-only for repeated identical calls.

Detector behavior comparison:

DetectorwarningThresholdcriticalThresholdglobalCircuitBreaker
global_circuit_breaker✅ Blocks (requires same args + same result)
known_poll_no_progress⚠️ Warns✅ Blocks
ping_pong⚠️ Warns✅ Blocks (with noProgressEvidence)
generic_repeat⚠️ Warns❌ Not checked❌ Not checked

OpenClaw version

2026.3.24 (cff6dc9)

Operating system

Ubuntu 22.04.5 LTS (Linux 5.15.0-173-generic, x64)

Install method

npm global

Model

Not model-specific (affects all models — this is a gateway-level loop detection issue)

Provider / routing chain

N/A (gateway-level, before provider routing)

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Impact and severity

  • Affected: All users with loop detection enabled who encounter non-polling tool loops (the most common runaway pattern)
  • Severity: High — the primary protection mechanism has a gap for the most common loop type
  • Frequency: Always — generic_repeat never blocks regardless of threshold settings
  • Consequence: Runaway token spend and session lockups. The global_circuit_breaker does not catch these loops because it requires identical results (not just identical arguments), so loops with varying output bypass it entirely. The only effect is a warning message injected into the agent's context, which the agent may ignore.

Additional information

Suggested fix: Add criticalThreshold escalation to the generic_repeat detector. When recentCount >= criticalThreshold, return level: "critical" instead of level: "warning". This aligns with the documented behavior and closes the protection gap.

Alternatively, update the documentation to clarify that criticalThreshold only applies to known_poll_no_progress and ping_pong detectors, and that generic_repeat is intentionally warn-only. However, this leaves a significant gap in loop protection.

Relevant source files:

  • src/agents/tool-loop-detection.tsdetectToolCallLoop() function, lines 344-360
  • docs/tools/loop-detection.mdcriticalThreshold field description

Config used during observation:

{
  "tools": {
    "loopDetection": {
      "enabled": true,
      "historySize": 30,
      "warningThreshold": 3,
      "criticalThreshold": 4,
      "globalCircuitBreakerThreshold": 5
    }
  }
}

extent analysis

TL;DR

The generic_repeat loop detector should be updated to check criticalThreshold and return level: "critical" when exceeded, to align with documented behavior and prevent runaway token spend.

Guidance

  • Review the detectToolCallLoop function in src/agents/tool-loop-detection.ts to understand the current implementation of the generic_repeat detector.
  • Update the generic_repeat detector to check criticalThreshold and return level: "critical" when recentCount >= criticalThreshold.
  • Verify that the updated detector behaves as expected by testing it with different threshold settings and loop patterns.
  • Consider updating the documentation to reflect the changed behavior of the generic_repeat detector.

Example

// In src/agents/tool-loop-detection.ts, update the generic_repeat detector
if (recentCount >= criticalThreshold) {
  return { level: "critical" };
} else if (recentCount >= warningThreshold) {
  return { level: "warning" };
}

Notes

The suggested fix assumes that the criticalThreshold value is correctly configured and that the generic_repeat detector is the primary protection mechanism for non-polling tool loops. Additional testing and verification may be necessary to ensure that the updated detector behaves as expected in all scenarios.

Recommendation

Apply the suggested fix to update the generic_repeat detector to check criticalThreshold and return level: "critical" when exceeded. This will align the detector's behavior with the documented behavior and prevent runaway token spend.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Per the documentation (docs/tools/loop-detection.md), criticalThreshold is described as "threshold for blocking repetitive loop patterns." The generic_repeat detector should escalate to level: "critical" (which blocks the tool call) when recentCount >= criticalThreshold, consistent with how known_poll_no_progress and ping_pong detectors behave.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING