openclaw - 💡(How to fix) Fix Approval-gate denials routed via followup-channel produce phantom 'missing tool result' synthetic placeholders

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…

When tools.exec.strictInlineEval: true is combined with tools.exec.ask: "off", exec commands containing inline-eval patterns (sed -n, python -c, node -e, awk -e, ruby -e, osascript -e) wait ~5-8 minutes at the approval boundary, are denied by enforceStrictInlineEvalApprovalBoundary, and the denial message is routed via sendExecApprovalFollowup to a separate delivery channel that never reaches the calling agent's tool_result stream. The transport-layer repairToolUseResultPairing then injects the synthetic placeholder [openclaw] missing tool result in session history; inserted synthetic error result for transcript repair. and the agent has no way to know an approval gate was even involved.

Error Message

When tools.exec.strictInlineEval: true is combined with tools.exec.ask: "off", exec commands containing inline-eval patterns (sed -n, python -c, node -e, awk -e, ruby -e, osascript -e) wait ~5-8 minutes at the approval boundary, are denied by enforceStrictInlineEvalApprovalBoundary, and the denial message is routed via sendExecApprovalFollowup to a separate delivery channel that never reaches the calling agent's tool_result stream. The transport-layer repairToolUseResultPairing then injects the synthetic placeholder [openclaw] missing tool result in session history; inserted synthetic error result for transcript repair. and the agent has no way to know an approval gate was even involved.

  1. Direct return: When enforceStrictInlineEvalApprovalBoundary denies a command, construct an error tool_result with the denial reason and return it through the original tool's response path instead of sendExecApprovalFollowup.
  2. Synthesize specifically: Have makeMissingToolResult distinguish between "truly missing" (race condition, transport error) and "explicitly denied" cases. The synthetic-error placeholder mentions "transcript repair," which misleads operators into investigating transport-layer bugs instead of approval-gate config.

Root Cause

Root cause (dist trace)

Fix Action

Workaround

Set tools.exec.strictInlineEval: false (must be edited directly in ~/.openclaw/openclaw.json — gateway config.patch rejects this as a protected path).

Code Example

"tools": {
     "exec": {
       "security": "full",
       "ask": "off",
       "strictInlineEval": true
     }
   }

---

sed -n '1,5p' /etc/hosts

---

if (!params.baseDecision.timedOut || !params.requiresInlineEvalApproval || !params.approvedByAsk) {
  return { approvedByAsk: params.approvedByAsk, deniedReason: params.deniedReason };
}
return { approvedByAsk: false, deniedReason: params.deniedReason ?? "approval-timeout" };
RAW_BUFFERClick to expand / collapse

Summary

When tools.exec.strictInlineEval: true is combined with tools.exec.ask: "off", exec commands containing inline-eval patterns (sed -n, python -c, node -e, awk -e, ruby -e, osascript -e) wait ~5-8 minutes at the approval boundary, are denied by enforceStrictInlineEvalApprovalBoundary, and the denial message is routed via sendExecApprovalFollowup to a separate delivery channel that never reaches the calling agent's tool_result stream. The transport-layer repairToolUseResultPairing then injects the synthetic placeholder [openclaw] missing tool result in session history; inserted synthetic error result for transcript repair. and the agent has no way to know an approval gate was even involved.

Version

openclaw 2026.5.27 (reproducible on HEAD per source review)

Repro

  1. Set in ~/.openclaw/openclaw.json:
    "tools": {
      "exec": {
        "security": "full",
        "ask": "off",
        "strictInlineEval": true
      }
    }
  2. From an agent session, issue an exec call containing an inline-eval form:
    sed -n '1,5p' /etc/hosts
  3. Wait ~5 minutes.
  4. Agent receives the synthetic [openclaw] missing tool result in session history instead of the real denial message.

Root cause (dist trace)

dist/bash-tools-DZlOuw96.js:564enforceStrictInlineEvalApprovalBoundary:

if (!params.baseDecision.timedOut || !params.requiresInlineEvalApproval || !params.approvedByAsk) {
  return { approvedByAsk: params.approvedByAsk, deniedReason: params.deniedReason };
}
return { approvedByAsk: false, deniedReason: params.deniedReason ?? "approval-timeout" };

Inline-eval commands explicitly override askFallback: "full" auto-approval on timeout. When denied via this path, the result goes through sendExecApprovalFollowupResult (dist/bash-tools-DZlOuw96.js around line 480), which delivers via a separate routing channel rather than returning as the tool's response.

dist/openai-transport-stream-BOZ_TYPY.js:702repairToolUseResultPairing then injects makeMissingToolResult at line 818 when the tool_use is not matched.

Expected behavior

The denial message ("Exec denied (...): approval-timeout: ...") should be returned as the tool_result for the original tool_use, not routed to a side-channel. The calling agent should see the actual reason for the failure.

Suggested fix

Either:

  1. Direct return: When enforceStrictInlineEvalApprovalBoundary denies a command, construct an error tool_result with the denial reason and return it through the original tool's response path instead of sendExecApprovalFollowup.
  2. Synthesize specifically: Have makeMissingToolResult distinguish between "truly missing" (race condition, transport error) and "explicitly denied" cases. The synthetic-error placeholder mentions "transcript repair," which misleads operators into investigating transport-layer bugs instead of approval-gate config.

Operator-visible impact

The misdiagnosis path is significant. An operator hitting this for the first time has no signal that approval gating is involved — the chat shows tool results disappearing with a transcript-repair message. We spent multiple hours misdiagnosing this as a transport-layer / parallel-batch race before discovering the approval-gate config conflict.

Workaround

Set tools.exec.strictInlineEval: false (must be edited directly in ~/.openclaw/openclaw.json — gateway config.patch rejects this as a protected path).

Related

We also observed a separate parallel-batch race in installSessionToolResultGuard that produces the same synthetic placeholder. Filing that separately.

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

The denial message ("Exec denied (...): approval-timeout: ...") should be returned as the tool_result for the original tool_use, not routed to a side-channel. The calling agent should see the actual reason for the failure.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Approval-gate denials routed via followup-channel produce phantom 'missing tool result' synthetic placeholders