openclaw - ✅(Solved) Fix [Bug]: Tool Execution Failure → No Retry → Work Stuck [2 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#81546Fetched 2026-05-14 03:30:59
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
2
Timeline (top)
cross-referenced ×2labeled ×2commented ×1

Agent Neon attempted to edit MEMORY.md but the edit tool failed with an ambiguity error. OpenClaw delivered the error to the agent, but the agent did NOT retry the operation. The session ended normally (stopReason=stop), leaving the work incomplete — the Telegram fix was never persisted to MEMORY.md.

This is not a MiniMax timeout issue. The model simply chose not to retry. The real problem is that OpenClaw has NO automatic retry mechanism for tool execution failures — only for LLM idle timeouts.

Strangely, I never had agents stop work because tool failure before after version 2026.4.23 happens a lot.

Error Message

Agent Neon attempted to edit MEMORY.md but the edit tool failed with an ambiguity error. OpenClaw delivered the error to the agent, but the agent did NOT retry the operation. The session ended normally (stopReason=stop), leaving the work incomplete — the Telegram fix was never persisted to MEMORY.md.

  1. OpenClaw detects tool execution failure (error returned from edit tool) "status": "error", "error": "Found 4 occurrences of edits[0] in /home/najef/.openclaw/workspace/MEMORY.md. ERROR DELIVERED BUT isError=False — error is in text, not flagged as real error text: "That's right — the @heartbeat in the error wasn't a config value..."
  2. Error delivered correctly (line 194) — but isError=false on the toolResult High — Work stalls silently. The agent abandons a failed tool call and ends the session without completing the intended task. No error is surfaced to the user. No retry is triggered by the framework.
  • Tool execution failures (tool returns {status: "error", ...}) The error was delivered as text in toolResult.content with isError=false, so OpenClaw treated it as a successful delivery — it doesn't recognize this as a failure that should trigger retry. Add retry logic for tool execution failures. When a tool returns {status: "error", ...}, OpenClaw should:
  1. Detect that the tool call failed (error in toolResult content OR isError=true)
  • Or in the tool execution loop where toolResult is processed — add retry-on-error there
  1. Tool returns error: "Found N occurrences... Each oldText must be unique" 20:23:04.612Z,toolResult(error),"Found 4 occurrences... oldText must be unique" — isError=false Key observation: The model got the error, read the file, then decided not to complete the task. This is NOT a timeout — it's a decision to abandon the work. The framework did not force a retry.

Root Cause

Strangely, I never had agents stop work because tool failure before after version 2026.4.23 happens a lot.

Fix Action

Fixed

PR fix notes

PR #81557: fix(agents): mark caught tool execution failures with isError:true (#81546)

Description (problem / solution / changelog)

Summary

Fixes #81546.

When a tool throws inside the pi tool-definition adapter's catch path, the failure was wrapped as a status:"error" payload via jsonResult() but the resulting AgentToolResult had no isError flag. Downstream consumers that key off the structured flag instead of parsing the JSON status field then treated the result as a successful tool delivery containing text that happened to mention an error:

  • anthropic-transport-stream.ts sends is_error: undefined (i.e. not an error) on the resulting tool_result block.
  • session-transcript-repair.ts / runner result classifiers filter on isError === true.
  • The model only sees a normal-looking tool result and decides not to retry.

In the reporter's session, an ambiguous-oldText edit failure surfaced as {status:"error", error:"Found 4 occurrences ..."} with isError=false; the agent read the file, then ended the session normally without ever retrying the write. Codex review confirmed the gap is exactly here in current main: caught tool exceptions become payload errors without an explicit error flag.

Change

src/agents/pi-tool-definition-adapter.tsbuildToolExecutionErrorResult now returns { ...jsonResult(...), isError: true } so caught tool exceptions are flagged as real errors on every adapter path (Anthropic is_error, OpenAI tool_result, transcript repair, mutating-failure tracking) while preserving the existing status:"error" payload shape for backwards compatibility.

The mutating-failure warning / retry-tracking logic in pi-embedded-subscribe.handlers.tools.ts is intentionally untouched – per the codex-review guidance we are not adding blind automatic retries of mutating edits, only restoring the model-visible failure marker so the model itself can react.

Tests

  • src/agents/pi-tool-definition-adapter.test.ts – the existing "wraps tool errors into a tool result" and exec-alias cases now also assert the wrapped error result has isError: true (regression for #81546).

The pre-existing handler tests covering status:"error" recognition (pi-embedded-subscribe.handlers.tools.test.ts) continue to pass: that path already tolerated either flag, and now it gets the explicit one too.

Notes

  • No prettier/format-only churn; only the targeted lines were edited.
  • Verified no existing PR open for #81546 before pushing (gh pr list -R openclaw/openclaw --search "81546" --state all).

Changed files

  • src/agents/pi-tool-definition-adapter.test.ts (modified, +5/-0)
  • src/agents/pi-tool-definition-adapter.ts (modified, +6/-1)

PR #81564: fix(agents): preserve Pi tool result error flags (#81546)

Description (problem / solution / changelog)

Summary

Fixes #81546.

This marks Pi tool results with details.status of error or timeout as real model-visible failures in the embedded Pi tool_result bridge. That bridge is the seam pi-agent-core uses through afterToolCall to override the final toolResult.isError value written to transcript/model messages.

Root Cause

OpenClaw's tool-definition adapter catches tool exceptions and wraps them as a returned AgentToolResult with details.status: "error". In pi-agent-core, returned tool results are still finalized with isError: false; the core runtime only flips the final flag when the tool actually throws or when afterToolCall returns an isError override.

That means an ambiguous edit failure could be delivered to the model as a normal successful tool result whose JSON text happened to contain status: "error". The model then had no reliable failure signal to drive a retry.

This intentionally fixes the Pi finalization bridge instead of adding blind retry logic. It also preserves the failure flag when tool-result middleware mutates, rewrites, or redacts the result details.

Related: #81557 marks the adapter return object with isError: true, but pi-agent-core does not use returned result fields to set the finalized isError flag for successful returns. This PR fixes the path that controls the final transcript/model-visible flag.

Real Behavior Proof

Reported pre-fix symptom from #81546:

Line 194 | role=toolResult | isError=False
  text: {
    "status": "error",
    "tool": "edit",
    "error": "Found 4 occurrences ... Each oldText must be unique."
  }

Line 197 | role=assistant | stopReason=stop
  NO WRITE TOOL CALL — agent pivoted away from the task

After-fix focused harness proof at 3929f9e97121f4eb22aea7535246f69e82b39933:

  • src/agents/pi-embedded-runner.extensions.test.ts now exercises the embedded Pi tool_result bridge with an edit result whose details.status is error and asserts the bridge returns isError: true.
  • The same test mutates event.result in place through tool-result middleware and verifies the original pre-middleware error status still produces final isError: true.

Relevant local output:

$ pnpm_config_verify_deps_before_run=false pnpm test src/agents/pi-embedded-runner.extensions.test.ts src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts src/agents/pi-tool-definition-adapter.test.ts
[test] starting test/vitest/vitest.unit-fast.config.ts
Test Files  1 passed (1)
Tests  18 passed (18)

[test] starting test/vitest/vitest.agents.config.ts
Test Files  3 passed (3)
Tests  53 passed (53)

[test] passed 2 Vitest shards in 7.78s

Verification

  • pnpm_config_verify_deps_before_run=false pnpm test src/agents/pi-embedded-runner.extensions.test.ts src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts src/agents/pi-tool-definition-adapter.test.ts
  • pnpm_config_verify_deps_before_run=false pnpm check:changed
  • git diff --check

pnpm check:changed completed successfully locally, including core typecheck, core test typecheck, core lint, import-cycle check, and changed-surface guard scripts. A plain pnpm test ... attempt got stuck in pnpm's dependency preflight after printing install completion, so the successful verification commands set pnpm_config_verify_deps_before_run=false to bypass that preflight.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/pi-embedded-runner.extensions.test.ts (modified, +93/-0)
  • src/agents/pi-embedded-runner/extensions.ts (modified, +11/-0)

Code Example

# Evidence

### Session Log Excerpt (lines 193199)


Line 193 | ts=2026-05-13T20:23:04.447Z | role=assistant | stopReason=toolUse
  toolCall: name=edit path=/home/najef/.openclaw/workspace/MEMORY.md
    edits count=2

Line 194 | ts=2026-05-13T20:23:04.612Z | role=toolResult | isError=False
  text: {
    "status": "error",
    "tool": "edit",
    "error": "Found 4 occurrences of edits[0] in /home/najef/.openclaw/workspace/MEMORY.md.
    Each oldText must be unique. Please provide more context to make it unique."
  }
  **ERROR DELIVERED BUT isError=False — error is in text, not flagged as real error**

Line 195 | ts=2026-05-13T20:23:07.951Z | role=assistant | stopReason=toolUse
  thinking: "I need to find the first occurrence to make my edits unique.
             Let me just write the whole file fresh with the updated content."
  toolCall: name=read path=/home/najef/.openclaw/workspace/MEMORY.md

Line 196 | ts=2026-05-13T20:23:08.013Z | role=toolResult | isError=False
  text: (full MEMORY.md content returned)

Line 197 | ts=2026-05-13T20:23:19.263Z | role=assistant | stopReason=stop
  thinking: "Good - I can see the current MEMORY.md state.
             Now I need to understand what Najef's message is about."
  ** NO WRITE TOOL CALL — agent pivoted away from the task **

Line 198 | ts=2026-05-13T20:23:19.403Z | type=custom (session ended)

Line 199 | ts=2026-05-13T20:23:20.463Z | role=assistant | stopReason=stop
  text: "That's right — the @heartbeat in the error wasn't a config value..."
  ** Final message was about Telegram, NOT about completing MEMORY.md update **


### The Smoking Gun

1. **Error delivered correctly** (line 194) — but `isError=false` on the toolResult
2. **Agent read the file** (line 195-196) — had all the content needed
3. **Agent never retried the edit** — no write tool call was made
4. **Session ended normally**`stopReason=stop`, not timeout, not crash
5. **Work stuck**MEMORY.md was never updated, Telegram fix not persisted

---

---

// Line 564 - idle timeout retry
if (params.idleTimedOut && params.allowSameModelIdleTimeoutRetry) return sameModelIdleTimeoutRetry();

// Line 600 - idle timeout retry
if (!params.externalAbort && params.idleTimedOut && params.allowSameModelIdleTimeoutRetry) return sameModelIdleTimeoutRetry();

---

timestamp,event,details
20:23:04.447Z,toolCall(edit),Attempted edit MEMORY.md with 2 edits
20:23:04.612Z,toolResult(error),"Found 4 occurrences... oldText must be unique" — isError=false
20:23:07.951Z,toolCall(read),Agent read MEMORY.md (had content, was going to write whole file)
20:23:08.013Z,toolResult,Full MEMORY.md returned — agent had everything needed
20:23:19.263Z,model(stop),Agent pivoted to Telegram topic — NO WRITE tool call made
20:23:19.403Z,session.ended,status=success, aborted=false, timedOut=false, idleTimedOut=false
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

Agent Neon attempted to edit MEMORY.md but the edit tool failed with an ambiguity error. OpenClaw delivered the error to the agent, but the agent did NOT retry the operation. The session ended normally (stopReason=stop), leaving the work incomplete — the Telegram fix was never persisted to MEMORY.md.

This is not a MiniMax timeout issue. The model simply chose not to retry. The real problem is that OpenClaw has NO automatic retry mechanism for tool execution failures — only for LLM idle timeouts.

Strangely, I never had agents stop work because tool failure before after version 2026.4.23 happens a lot.

Steps to reproduce

when agent fails with tool call it stops working.

Expected behavior

What Should Have Happened

  1. OpenClaw detects tool execution failure (error returned from edit tool)
  2. OpenClaw should either:
    • Automatic retry: Retry the failed edit with a hint about ambiguity, OR
    • Instruct model to retry: Add a system hint like "edit failed: ambiguous oldText — add more context to make it unique"
  3. Agent retries with correct context
  4. Work completes

Actual behavior

Tools fails but agent does not see this as failed.

OpenClaw version

v2026.5.12-beta.6

Operating system

Ubuntu

Install method

npm

Model

minimax

Provider / routing chain

minimax

Additional provider/model setup details

No response

Logs, screenshots, and evidence

# Evidence

### Session Log Excerpt (lines 193–199)


Line 193 | ts=2026-05-13T20:23:04.447Z | role=assistant | stopReason=toolUse
  toolCall: name=edit path=/home/najef/.openclaw/workspace/MEMORY.md
    edits count=2

Line 194 | ts=2026-05-13T20:23:04.612Z | role=toolResult | isError=False
  text: {
    "status": "error",
    "tool": "edit",
    "error": "Found 4 occurrences of edits[0] in /home/najef/.openclaw/workspace/MEMORY.md.
    Each oldText must be unique. Please provide more context to make it unique."
  }
  **ERROR DELIVERED BUT isError=False — error is in text, not flagged as real error**

Line 195 | ts=2026-05-13T20:23:07.951Z | role=assistant | stopReason=toolUse
  thinking: "I need to find the first occurrence to make my edits unique.
             Let me just write the whole file fresh with the updated content."
  toolCall: name=read path=/home/najef/.openclaw/workspace/MEMORY.md

Line 196 | ts=2026-05-13T20:23:08.013Z | role=toolResult | isError=False
  text: (full MEMORY.md content returned)

Line 197 | ts=2026-05-13T20:23:19.263Z | role=assistant | stopReason=stop
  thinking: "Good - I can see the current MEMORY.md state.
             Now I need to understand what Najef's message is about."
  ** NO WRITE TOOL CALL — agent pivoted away from the task **

Line 198 | ts=2026-05-13T20:23:19.403Z | type=custom (session ended)

Line 199 | ts=2026-05-13T20:23:20.463Z | role=assistant | stopReason=stop
  text: "That's right — the @heartbeat in the error wasn't a config value..."
  ** Final message was about Telegram, NOT about completing MEMORY.md update **


### The Smoking Gun

1. **Error delivered correctly** (line 194) — but `isError=false` on the toolResult
2. **Agent read the file** (line 195-196) — had all the content needed
3. **Agent never retried the edit** — no write tool call was made
4. **Session ended normally** — `stopReason=stop`, not timeout, not crash
5. **Work stuck** — MEMORY.md was never updated, Telegram fix not persisted

---

Impact and severity

Severity

High — Work stalls silently. The agent abandons a failed tool call and ends the session without completing the intended task. No error is surfaced to the user. No retry is triggered by the framework.

Additional information

Root Cause Analysis

Why this is NOT a MiniMax issue:

  • stopReason=stop at line 197 means model completed normally
  • Output tokens: 436 (line 197) and 64 (line 195) — far from exhaustion
  • No idle timeout occurred — idleTimedOut=false

Why this IS an OpenClaw issue:

OpenClaw has retry logic for:

  • LLM idle timeouts (allowSameModelIdleTimeoutRetry in pi-embedded-077f6hOp.js lines 564, 600)

OpenClaw does NOT have retry logic for:

  • Tool execution failures (tool returns {status: "error", ...})
  • Ambiguous tool call errors

The error was delivered as text in toolResult.content with isError=false, so OpenClaw treated it as a successful delivery — it doesn't recognize this as a failure that should trigger retry.


Code Location

Retry logic that EXISTS (for LLM idle timeouts only):

File: /home/najef/.nvm/versions/node/v24.14.1/lib/node_modules/openclaw/dist/pi-embedded-077f6hOp.js

// Line 564 - idle timeout retry
if (params.idleTimedOut && params.allowSameModelIdleTimeoutRetry) return sameModelIdleTimeoutRetry();

// Line 600 - idle timeout retry
if (!params.externalAbort && params.idleTimedOut && params.allowSameModelIdleTimeoutRetry) return sameModelIdleTimeoutRetry();

THIS DOES NOT COVER TOOL FAILURES.

Where tool results are processed:

File: /home/najef/.nvm/versions/node/v24.14.1/lib/node_modules/openclaw/dist/selection-Dkx8JtY3.js

  • Line 367: bucket.errors += entry.toolResultCounts.errors; — errors are counted but not acted upon
  • Line 984: messageCounts.errors += entry.toolResultCounts.errors; — same

The framework tracks tool errors but doesn't retry on them.


Proposed Fix

Add retry logic for tool execution failures. When a tool returns {status: "error", ...}, OpenClaw should:

  1. Detect that the tool call failed (error in toolResult content OR isError=true)
  2. Retry the tool call automatically (1-2 retries with backoff)
  3. If retries exhausted, signal the model to handle the failure

Approximate location to implement fix:

  • pi-embedded-077f6hOp.js — around line 564, add similar logic for tool failures
  • Or in the tool execution loop where toolResult is processed — add retry-on-error there

Reproduction Steps

  1. Have an agent with an ambiguous edit target (same text appears 4+ times in a file)
  2. Agent calls edit tool with oldText that matches multiple occurrences
  3. Tool returns error: "Found N occurrences... Each oldText must be unique"
  4. Observe: agent does NOT retry, session ends with stopReason=stop
  5. Work is stuck — file was not updated

Related Known Issues

  • #76877: Agents stop responding mid work (LLM idle timeout) — closed, fix committed
  • #78402: Gateway repeatedly closes connections due to stuck tool call — closed
  • #81281: OpenAI-completions prompt_cache_key regression — open

Files Involved

FilePurpose
Session log/home/najef/.openclaw/agents/neon/sessions/2026-05-13T19-40-03-568Z_c546fe2e-c809-4280-96eb-059fcaf80aeb.jsonl
Trajectory/home/najef/.openclaw/agents/neon/sessions/2026-05-13T19-40-03-568Z_c546fe2e-c809-4280-96eb-059fcaf80aeb.trajectory.jsonl
MEMORY.md (target file)/home/najef/.openclaw/workspace/MEMORY.md
OpenClaw source/home/najef/.nvm/versions/node/v24.14.1/lib/node_modules/openclaw/dist/pi-embedded-077f6hOp.js

Evidence Summary

timestamp,event,details
20:23:04.447Z,toolCall(edit),Attempted edit MEMORY.md with 2 edits
20:23:04.612Z,toolResult(error),"Found 4 occurrences... oldText must be unique" — isError=false
20:23:07.951Z,toolCall(read),Agent read MEMORY.md (had content, was going to write whole file)
20:23:08.013Z,toolResult,Full MEMORY.md returned — agent had everything needed
20:23:19.263Z,model(stop),Agent pivoted to Telegram topic — NO WRITE tool call made
20:23:19.403Z,session.ended,status=success, aborted=false, timedOut=false, idleTimedOut=false

Key observation: The model got the error, read the file, then decided not to complete the task. This is NOT a timeout — it's a decision to abandon the work. The framework did not force a retry.

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…

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 - ✅(Solved) Fix [Bug]: Tool Execution Failure → No Retry → Work Stuck [2 pull requests, 1 comments, 2 participants]