Files
openclaw/docs/tools/loop-detection.md
Vincent Koc 180e295dc6 docs(tools): rewrite loop detection, code execution, and tighten elevated/skills
Loop detection (docs/tools/loop-detection.md): substantial rewrite.
Fixed the post-compaction guard default story — the guard runs whenever
tools.loopDetection.enabled is not explicitly false, even with no
config block at all (verified in src/agents/pi-embedded-runner/run.ts
near line 800: 'enabled: resolvedLoopDetectionConfig?.enabled !==
false'). The previous doc framed it as opt-in. Added the missing
unknownToolThreshold field (default 10) sourced from
src/config/schema.help.ts, a complete fields table, and a CardGroup
related links section.

Code execution (docs/tools/code-execution.md): rewrote with
Steps-driven setup, code-verified defaults from
extensions/xai/src/code-execution-shared.ts (default model
grok-4-1-fast, default timeout 30 s, optional maxTurns), the
missing_xai_api_key structured error documented as JSON, and a
properties summary table. Replaced the trailing bullet list with a
CardGroup pointing at exec, exec-approvals, web tools, and the xAI
provider page.

Elevated (docs/tools/elevated.md): converted Related to a CardGroup
and added a Note that the bash chat command (! prefix / /bash alias)
also requires tools.elevated, sourced from
src/config/schema.help.ts:1375.

Skills config (docs/tools/skills-config.md): renamed the
'Sandboxed skills + env vars' subhead to remove the brittle '+'
character per docs/CLAUDE.md, promoted the host-only env warning to a
Warning block so the most common skill-config footgun stays visible,
and converted Related to a CardGroup including a config-reference
link.
2026-05-05 16:49:29 -07:00

8.2 KiB

summary, title, read_when
summary title read_when
How to enable and tune guardrails that detect repetitive tool-call loops Tool-loop detection
A user reports agents getting stuck repeating tool calls
You need to tune repetitive-call protection
You are editing agent tool/runtime policies
You hit `compaction_loop_persisted` aborts after a context-overflow retry

OpenClaw has two cooperating guardrails for repetitive tool-call patterns:

  1. Loop detection (tools.loopDetection.enabled) — disabled by default. Watches the rolling tool-call history for repeated patterns and unknown-tool retries.
  2. Post-compaction guard (tools.loopDetection.postCompactionGuard) — enabled by default unless tools.loopDetection.enabled is explicitly false. Arms after every compaction-retry and aborts the run when the agent emits the same (tool, args, result) triple within the window.

Both are configured under the same tools.loopDetection block, but the post-compaction guard runs whenever the master switch is not explicitly off. Set tools.loopDetection.enabled: false to silence both surfaces.

Why this exists

  • Detect repetitive sequences that do not make progress.
  • Detect high-frequency no-result loops (same tool, same inputs, repeated errors).
  • Detect specific repeated-call patterns for known polling tools.
  • Prevent context-overflow then compaction then same-loop cycles from running indefinitely.

Configuration block

Global defaults, with every documented field shown:

{
  tools: {
    loopDetection: {
      enabled: false, // master switch for the rolling-history detectors
      historySize: 30,
      warningThreshold: 10,
      criticalThreshold: 20,
      unknownToolThreshold: 10,
      globalCircuitBreakerThreshold: 30,
      detectors: {
        genericRepeat: true,
        knownPollNoProgress: true,
        pingPong: true,
      },
      postCompactionGuard: {
        windowSize: 3, // armed after compaction-retry; runs unless enabled is explicitly false
      },
    },
  },
}

Per-agent override (optional):

{
  agents: {
    list: [
      {
        id: "safe-runner",
        tools: {
          loopDetection: {
            enabled: true,
            warningThreshold: 8,
            criticalThreshold: 16,
          },
        },
      },
    ],
  },
}

Field behavior

Field Default Effect
enabled false Master switch for the rolling-history detectors. Setting false also disables the post-compaction guard.
historySize 30 Number of recent tool calls kept for analysis.
warningThreshold 10 Threshold before a pattern is classified as warning-only.
criticalThreshold 20 Threshold for blocking repetitive loop patterns.
unknownToolThreshold 10 Block repeated calls to the same unavailable tool after this many misses.
globalCircuitBreakerThreshold 30 Global no-progress breaker threshold across all detectors.
detectors.genericRepeat true Detects repeated same-tool + same-params patterns.
detectors.knownPollNoProgress true Detects known polling-like patterns with no state change.
detectors.pingPong true Detects alternating ping-pong patterns.
postCompactionGuard.windowSize 3 Number of post-compaction tool calls during which the guard stays armed and the count of identical triples that aborts the run.

For exec, no-progress checks compare stable command outcomes and ignore volatile runtime metadata such as duration, PID, session ID, and working directory. When a run id is available, recent tool-call history is evaluated only within that run so scheduled heartbeat cycles and fresh runs do not inherit stale loop counts from earlier runs.

  • For smaller models, set enabled: true and leave the thresholds at their defaults. Flagship models rarely need rolling-history detection and can leave the master switch at false while still benefiting from the post-compaction guard.
  • Keep thresholds ordered as warningThreshold < criticalThreshold < globalCircuitBreakerThreshold.
  • If false positives occur:
    • Raise warningThreshold and/or criticalThreshold.
    • Optionally raise globalCircuitBreakerThreshold.
    • Disable only the specific detector causing issues (detectors.<name>: false).
    • Reduce historySize for less strict historical context.
  • To disable everything (including the post-compaction guard), set tools.loopDetection.enabled: false explicitly.

Post-compaction guard

When the runner completes a compaction-retry after a context-overflow, it arms a short-window guard that watches the next few tool calls. If the agent emits the same (toolName, argsHash, resultHash) triple multiple times within the window, the guard concludes that compaction did not break the loop and aborts the run with a compaction_loop_persisted error.

The guard is gated by the master tools.loopDetection.enabled flag with one twist: it stays enabled when the flag is unset or true and only deactivates when the flag is explicitly false. This is intentional. The guard exists to escape compaction loops that would otherwise burn unbounded tokens, so a no-config user still gets the protection.

{
  tools: {
    loopDetection: {
      // master switch; set false to disable the guard along with the rolling detectors
      enabled: true,
      postCompactionGuard: {
        windowSize: 3, // default
      },
    },
  },
}
  • Lower windowSize is stricter (fewer attempts before abort).
  • Higher windowSize gives the agent more recovery attempts.
  • The guard never aborts when results are changing, only when results are byte-identical across the window.
  • It is intentionally narrow: it fires only in the immediate aftermath of a compaction-retry.
The post-compaction guard runs whenever the master flag is not explicitly `false`, even if you never wrote a `tools.loopDetection` block. To verify, look for `post-compaction guard armed for N attempts` in the gateway log immediately after a compaction event.

Logs and expected behavior

When a loop is detected, OpenClaw reports a loop event and either dampens or blocks the next tool-cycle depending on severity. This protects users from runaway token spend and lockups while preserving normal tool access.

  • Warnings come first.
  • Suppression follows when patterns persist past the warning threshold.
  • Critical thresholds block the next tool-cycle and surface a clear loop-detection reason in the run record.
  • The post-compaction guard emits compaction_loop_persisted errors with the offending tool name and identical-call count.
Allow/deny policy for shell execution. Reasoning effort levels and provider-policy interaction. Spawning isolated agents to bound runaway behavior. Full `tools.loopDetection` schema and merging semantics.