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.
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 |
|
OpenClaw has two cooperating guardrails for repetitive tool-call patterns:
- Loop detection (
tools.loopDetection.enabled) — disabled by default. Watches the rolling tool-call history for repeated patterns and unknown-tool retries. - Post-compaction guard (
tools.loopDetection.postCompactionGuard) — enabled by default unlesstools.loopDetection.enabledis explicitlyfalse. 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.
Recommended setup
- For smaller models, set
enabled: trueand leave the thresholds at their defaults. Flagship models rarely need rolling-history detection and can leave the master switch atfalsewhile still benefiting from the post-compaction guard. - Keep thresholds ordered as
warningThreshold < criticalThreshold < globalCircuitBreakerThreshold. - If false positives occur:
- Raise
warningThresholdand/orcriticalThreshold. - Optionally raise
globalCircuitBreakerThreshold. - Disable only the specific detector causing issues (
detectors.<name>: false). - Reduce
historySizefor less strict historical context.
- Raise
- To disable everything (including the post-compaction guard), set
tools.loopDetection.enabled: falseexplicitly.
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
windowSizeis stricter (fewer attempts before abort). - Higher
windowSizegives 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.
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_persistederrors with the offending tool name and identical-call count.