Files
openclaw/docs/tools/loop-detection.md
ruel225 cd321938f6 fix(agents): strip external-content nonce so loop detection blocks repeated wrapped failures (#130261)
* fix(agents): strip external-content nonce before loop-detection resultHash

External-content wrappers carry a fresh anti-forgery nonce per result
(createExternalContentMarkerId). The loop detector hashed the wrapper text
including the nonce, so identical failing wrapped tool calls never reached
the no-progress block threshold — the detector warned but never blocked,
leaving the agent to loop until it gave up (#130210).

Strip the wrapper nonce before hashing in extractTextContent, mirroring
the existing stripVolatileSendIds treatment of per-call send ids (#89090).
Only the hash is normalized; the delivered wrapper keeps its nonce, so
anti-forgery is unaffected.

Closes #130210

Co-Authored-By: Claude <noreply@anthropic.com>

* test(agents): validate protected error marker capture

Narrow the generated marker capture before recording it so the real network-error loop regression also passes its owning test-type graph.

---------

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 21:22:39 -07:00

6.4 KiB

summary, title, read_when
summary title read_when
How to enable guardrails that detect repetitive tool-call loops Tool-loop detection
A user reports agents getting stuck repeating tool calls
You need to control 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 against repetitive tool-call patterns, both configured under tools.loopDetection:

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

Set tools.loopDetection.enabled: false to silence both guardrails.

Why this exists

  • Detect repetitive sequences that make no progress.
  • Detect high-frequency no-result loops (same tool, same inputs, repeated errors).
  • Detect specific repeated-call patterns for known polling tools.
  • Break context-overflow -> compaction -> same-loop cycles instead of letting them run indefinitely.

Configuration block

Global setting:

{
  tools: {
    loopDetection: {
      enabled: false, // master switch for the rolling-history detectors
    },
  },
}

Per-agent override (optional, at agents.entries.*.tools.loopDetection):

{
  agents: {
    entries: {
      "safe-runner": {
        default: true,
        tools: {
          loopDetection: {
            enabled: true,
          },
        },
      },
    },
  },
}

The per-agent setting overrides the global setting.

You can also enable the global rolling-history detectors in Settings -> Labs in the Control UI.

Field behavior

Field Default Effect
enabled false Master switch for the rolling-history detectors. false also disables the post-compaction guard.

For exec, no-progress hashing compares stable command outcomes (status, exit code, timed-out flag, output) and ignores volatile runtime metadata such as duration, PID, session ID, and working directory. Outbound message-send results are hashed with volatile per-call ids (message id, file id, timestamp) stripped, so delivery IDs alone do not make repeated equivalent sends look like progress. When a run id is available, history is evaluated only within that run, so scheduled heartbeat cycles and fresh runs do not inherit stale loop counts from earlier runs.

Outcome comparisons also ignore fresh external-content wrapper nonces, including wrapped errors and JSON results. Delivered security markers remain unchanged; payload text, status, timestamps, and durations still distinguish network results. This is a syntactic comparison: literal or copied text matching the complete wrapper format also ignores nonce-only changes. It does not authenticate content, change authorization, or modify delivered tool results.

  • For smaller models, set enabled: true. Flagship models rarely need rolling-history detection and can leave the master switch unset while still benefiting from the post-compaction guard.
  • To disable everything, including the post-compaction guard, set tools.loopDetection.enabled: false explicitly.

Post-compaction guard

After a compaction-retry following a context-overflow, the runner arms a short-window guard on the next few tool calls. If the agent emits the same (toolName, argsHash, resultHash) triple enough times within that window, the guard concludes 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 turns off 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,
    },
  },
}
  • The guard compares normalized outcome hashes, not raw result bytes. Meaningful changes keep it from aborting; fresh wrapper nonces alone do not count as progress.
  • It only arms in the immediate aftermath of a compaction-retry, not at other points in a run.
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 logs a loop event and either warns or blocks the next tool-cycle depending on severity, protecting against runaway token spend and lockups while preserving normal tool access.

  • Warnings come first.
  • Blocking follows once a pattern persists past the warning threshold.
  • In the embedded agent loop, the first critical loop blocks the whole tool batch before any tool in that batch runs. The model then gets one more response with its normal tools.
  • During that response, the model can answer, ask a question, or continue with a different tool or different arguments.
  • Another critical loop in the same run blocks its whole batch and ends the run. A new user run starts with a fresh recovery allowance.
  • The post-compaction guard emits compaction_loop_persisted errors naming the offending tool 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.