docs/tools/loop-detection.md
OpenClaw has two cooperating guardrails for repetitive tool-call patterns:
tools.loopDetection.enabled) — disabled by default. Watches the rolling tool-call history for repeated patterns and unknown-tool retries.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.
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 | 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 no-progress 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 | Warns on repeated same-tool + same-params patterns and blocks when the same calls also return identical outcomes. |
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.
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.warningThreshold < criticalThreshold < globalCircuitBreakerThreshold.warningThreshold and/or criticalThreshold.globalCircuitBreakerThreshold.detectors.<name>: false).historySize for less strict historical context.tools.loopDetection.enabled: false explicitly.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
},
},
},
}
windowSize is stricter (fewer attempts before abort).windowSize gives the agent more recovery attempts.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.
compaction_loop_persisted errors with the offending tool name and identical-call count.