openclaw - ✅(Solved) Fix [Bug]: Agent enters infinite /approve exec loop, burning 965 API calls over 58 minutes [1 pull requests, 1 comments, 2 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#57432Fetched 2026-04-08 01:49:44
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
0
Author
Participants
Timeline (top)
closed ×2commented ×1cross-referenced ×1locked ×1

When exec returns approval-pending, the agent attempts to resolve it by running /approve <id> allow-always via the exec tool instead of presenting it to the user. This triggers another approval-pending, creating an infinite loop. Observed: 965 /approve exec calls over 58 minutes, consuming significant API budget with no user-visible output.

Error Message

Session file evidence (reset backup):

File: 5adec4b3-7cab-40bb-8390-169162f40ea8.jsonl.reset.2026-03-30T02-53-39.109Z
Size: 1621KB
Total tool calls: 969
/approve loop calls: 965
First timestamp: 2026-03-30T01:54:47.069Z (09:54 CST)
Last timestamp:  2026-03-30T02:52:50.459Z (10:52 CST)
Duration: 58 minutes

Sample loop pattern from session log:

TOOL_CALL: /approve <id> allow-always
APPROVAL: Approval required (id <new-id>). Command: /approve <prev-id> allow-always
TOOL_CALL: /approve <new-id> allow-always
APPROVAL: Approval required (id <newer-id>). Command: /approve <new-id> allow-always
... (repeats 965 times)

Root Cause

Root cause (identified)

Two instructions in src/agents/system-prompt.ts conflict:

PR fix notes

PR #57568: fix(exec): prevent infinite /approve loop when exec returns approval-pending

Description (problem / solution / changelog)

Summary

  • Problem: When exec returns approval-pending, the agent interprets the /approve command as a shell command and re-executes it via the exec tool. Each re-execution triggers a new approval-pending, creating an infinite loop. Observed: 965 /approve exec calls over 58 minutes, consuming ~$5 in API budget, with the session file growing to 1.6 MB and all user messages silently dropped. (Issue #57432)

  • Root Cause: Two conflicting instructions in src/agents/system-prompt.ts (lines 452–453):

    • Line 452: "When a first-class tool exists for an action, use the tool directly instead of asking the user to run equivalent CLI or slash commands."
    • Line 453: "When exec returns approval-pending, include the concrete /approve command from tool output…"

    The word "include" is ambiguous. The model follows line 452's directive ("use the tool directly") and interprets "include" as "execute via exec tool" rather than "show to the user as a chat message." Additionally, there is no guard in the exec tool to reject /approve commands, and no circuit breaker to detect the same command being repeated hundreds of times.

  • Fix: Three-layer defense to eliminate the loop:

    1. System prompt clarification (system-prompt.ts:453): Replaced ambiguous "include" with explicit "show … to the user as a chat message" and added NEVER execute /approve via the exec tool — /approve is a slash command handled by the chat input, not a shell command.
    2. Approval-pending message reinforcement (bash-tools.exec-runtime.ts): Appended IMPORTANT: Show this /approve command to the user as a chat message. Do NOT execute /approve via the exec tool. to the tool result text returned to the model.
    3. Exec tool hard guard (bash-tools.exec.ts): Added an early-exit check in createExecTool's execute function — if the command matches /^\s*\/approve\b/, throw an immediate error explaining that /approve is a chat slash command. This is the definitive backstop: even if a model ignores prompt instructions, the loop cannot start.

    This layered approach avoids side effects: the regex only matches commands that start with /approve (with optional leading whitespace), so legitimate commands like echo /approve are unaffected. The prompt change is scoped to the single approval-pending instruction line and does not alter any other tool-use guidance.

  • What changed:

    • src/agents/system-prompt.ts — Rewrote line 453 to disambiguate "include" → "show to user" and added explicit prohibition
    • src/agents/bash-tools.exec-runtime.ts — Added one line to buildApprovalPendingMessage() reinforcing the prohibition in tool output
    • src/agents/bash-tools.exec.ts — Added /approve command guard in createExecTool() execute function (early throw)
    • src/agents/system-prompt.test.ts — Added test verifying prompt contains the new prohibition language
    • src/agents/bash-tools.exec-runtime.test.ts — Added tests for buildApprovalPendingMessage() output
    • src/agents/bash-tools.test.ts — Added tests for exec /approve guard (rejection + non-false-positive)
  • What did NOT change (scope boundary):

    • No changes to the approval registration/decision flow (bash-tools.exec-approval-request.ts, bash-tools.exec-approval-followup.ts)
    • No changes to the embedded runner loop (pi-embedded-runner/run.ts, pi-embedded-subscribe.ts)
    • No changes to the deterministicApprovalPromptSent state machine
    • No changes to any other system prompt instructions (lines 452, 454, 455 untouched)
    • No changes to gateway allowlist processing or elevated exec logic

Reproduction

  1. Start OpenClaw with google/gemini-3.1-flash-lite-preview (or similar model)
  2. Send a message that causes the agent to run an exec command requiring approval
  3. Observe: agent receives approval-pending from exec, then calls exec again with /approve <id> allow-always as the shell command
  4. This generates a new approval-pending, and the loop repeats indefinitely
  5. After fix: step 3 now throws an immediate error, and the prompt instructs the model to show /approve to the user instead

Risk / Mitigation

  • Risk: A legitimate shell command starting with /approve would be blocked. In practice, no POSIX or common shell command starts with /approve — it is exclusively an OpenClaw slash command. The regex ^\s*\/approve\b is intentionally narrow (start-of-string only) to avoid false positives on commands like echo /approve.
  • Mitigation: Three test cases verify the guard: (1) /approve is rejected, (2) /approve with leading whitespace is rejected, (3) echo /approve is NOT rejected. The prompt and tool-output changes are additive and do not alter any existing behavior for non-approval flows.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway
  • Agent System Prompt
  • Exec Tool
  • Tests

Linked Issue/PR

Fixes #57432

Changed files

  • src/agents/bash-tools.exec-runtime.test.ts (modified, +38/-0)
  • src/agents/bash-tools.exec-runtime.ts (modified, +3/-0)
  • src/agents/bash-tools.exec.ts (modified, +137/-6)
  • src/agents/bash-tools.test.ts (modified, +260/-0)
  • src/agents/system-prompt.test.ts (modified, +12/-0)
  • src/agents/system-prompt.ts (modified, +1/-1)

Code Example

File: 5adec4b3-7cab-40bb-8390-169162f40ea8.jsonl.reset.2026-03-30T02-53-39.109Z
Size: 1621KB
Total tool calls: 969
/approve loop calls: 965
First timestamp: 2026-03-30T01:54:47.069Z (09:54 CST)
Last timestamp:  2026-03-30T02:52:50.459Z (10:52 CST)
Duration: 58 minutes

---

TOOL_CALL: /approve <id> allow-always
APPROVAL: Approval required (id <new-id>). Command: /approve <prev-id> allow-always
TOOL_CALL: /approve <new-id> allow-always
APPROVAL: Approval required (id <newer-id>). Command: /approve <new-id> allow-always
... (repeats 965 times)
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

When exec returns approval-pending, the agent attempts to resolve it by running /approve <id> allow-always via the exec tool instead of presenting it to the user. This triggers another approval-pending, creating an infinite loop. Observed: 965 /approve exec calls over 58 minutes, consuming significant API budget with no user-visible output.

Steps to reproduce

  1. Start OpenClaw with google/gemini-3.1-flash-lite-preview (or similar model).
  2. Send a message that causes the agent to run an exec command requiring approval.
  3. Observe: agent receives approval-pending from exec, then calls exec again with /approve <id> allow-always as the shell command.
  4. This generates a new approval-pending, and the loop repeats indefinitely.

Expected behavior

When exec returns approval-pending, the agent should present the /approve <id> allow-once|allow-always|deny command to the user as a chat message, and wait. The user sends the /approve reply, which is handled by the slash command handler — not by the exec tool.

Actual behavior

The agent calls exec with /approve <id> allow-always as the shell command. The exec tool itself requires approval, generating a new approval-pending. The agent loops: 965 /approve exec calls observed over 58 minutes (09:54–10:52 CST), session file grew to 1.6MB, with no output to the user.

Root cause (identified)

Two instructions in src/agents/system-prompt.ts conflict:

  • Line 452: "When a first-class tool exists for an action, use the tool directly instead of asking the user to run equivalent CLI or slash commands."
  • Line 453: "When exec returns approval-pending, include the concrete /approve command from tool output (with allow-once|allow-always|deny) and do not ask for a different or rotated code."

The model interprets "include" as "execute via exec tool" (following line 452's directive to use tools directly), rather than "show to user as a chat message". Fix: clarify line 453 to explicitly state /approve must be sent to the user as a chat message and must NOT be executed via exec.

Additionally, there is no loop detection or circuit breaker — the same exec command can repeat hundreds of times without any limit.

OpenClaw version

2026.3.30 (acea28a)

Operating system

macOS 25.4.0

Install method

pnpm dev

Model

google/gemini-3.1-flash-lite-preview

Provider / routing chain

openclaw -> google

Logs

Session file evidence (reset backup):

File: 5adec4b3-7cab-40bb-8390-169162f40ea8.jsonl.reset.2026-03-30T02-53-39.109Z
Size: 1621KB
Total tool calls: 969
/approve loop calls: 965
First timestamp: 2026-03-30T01:54:47.069Z (09:54 CST)
Last timestamp:  2026-03-30T02:52:50.459Z (10:52 CST)
Duration: 58 minutes

Sample loop pattern from session log:

TOOL_CALL: /approve <id> allow-always
APPROVAL: Approval required (id <new-id>). Command: /approve <prev-id> allow-always
TOOL_CALL: /approve <new-id> allow-always
APPROVAL: Approval required (id <newer-id>). Command: /approve <new-id> allow-always
... (repeats 965 times)

Impact and severity

  • Affected: Any user whose agent hits an exec approval prompt in TUI or non-interactive sessions
  • Severity: Critical — silently burns API budget, blocks the session, swallows user messages
  • Frequency: Reproducible whenever exec approval is triggered with no pre-approved allowlist entry
  • Consequence: ~$5 USD in wasted API calls (gemini-3.1-flash-lite-preview) in a single session; user messages silently dropped; session requires manual /reset to recover

Suggested fixes

  1. Clarify system prompt line 453: make explicit that /approve is a user-facing chat command, not a shell command to be exec'd.
  2. Add loop/circuit breaker: if the same command (or command pattern) is rejected/approval-pending N times consecutively, abort and surface an error to the user.
  3. Exempt /approve from exec: detect /approve ... as input to the exec tool and return an error immediately instead of attempting to run it.

extent analysis

Fix Plan

To resolve the issue, we will implement the following steps:

  • Clarify the system prompt in src/agents/system-prompt.ts to explicitly state that /approve must be sent to the user as a chat message.
  • Add a loop/circuit breaker to prevent the same command from being repeated consecutively.
  • Exempt /approve from being executed via the exec tool.

Code Changes

// src/agents/system-prompt.ts
// Clarify line 453 to explicitly state /approve must be sent to the user as a chat message
const approvalPrompt = `Please respond with /approve <id> allow-once|allow-always|deny`;

// Add loop/circuit breaker
const maxConsecutiveRejections = 5;
let consecutiveRejections = 0;

// Exempt /approve from exec
if (command.startsWith('/approve')) {
  return `Error: /approve cannot be executed via exec tool`;
}

// Update exec tool to handle approval-pending responses
if (response === 'approval-pending') {
  // Send /approve command to user as a chat message
  sendMessage(approvalPrompt);
  consecutiveRejections = 0;
} else if (response === 'rejected') {
  consecutiveRejections++;
  if (consecutiveRejections >= maxConsecutiveRejections) {
    // Abort and surface an error to the user
    sendMessage('Error: Maximum consecutive rejections reached');
  }
}

Verification

To verify that the fix worked, test the following scenarios:

  • Trigger an exec approval prompt and verify that the /approve command is sent to the user as a chat message.
  • Repeat the same command consecutively and verify that the loop/circuit breaker aborts and surfaces an error to the user after the maximum allowed attempts.
  • Attempt to execute /approve via the exec tool and verify that an error is returned immediately.

Extra Tips

  • Consider adding additional logging and monitoring to detect and prevent similar issues in the future.
  • Review the system prompt and exec tool documentation to ensure that they are clear and concise to prevent similar misinterpretations.

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

When exec returns approval-pending, the agent should present the /approve <id> allow-once|allow-always|deny command to the user as a chat message, and wait. The user sends the /approve reply, which is handled by the slash command handler — not by the exec tool.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING