Skip to content

bug: appendMissingToolResults synthesizes isError:true for tool results never written to storage #85668

Description

@Blakeshannon

Summary

When a tool result is never persisted to message storage (e.g., due to a crash, abort, or compaction gap), the appendMissingToolResults function in transport-message-transform.ts synthesizes a placeholder with isError: true during history replay. This causes the model to treat the tool call as failed, when the actual state is unknown/ambiguous — the result simply wasn't recorded.

Root Cause

appendMissingToolResults (currently in anthropic-vertex-stream-YyrYtvts.js) fires during message history reconstruction. It walks the stored message sequence and plugs any tool call that has no corresponding toolResult record. Current behavior:

result.push({
  role: "toolResult",
  toolCallId: toolCall.id,
  toolName: toolCall.name,
  content: [{ type: "text", text: "No result provided" }],
  isError: true,  // ← wrong: treats unknown as failure
  timestamp: Date.now()
});

The exec tool itself always emits a proper result ("(no output)" + exit code) on success. The gap occurs upstream — the result record was never written to storage. The model did receive the result in-context during the original turn, but on subsequent turns the replayed history shows a failure, causing the model to:

  1. Conclude the previous tool call failed
  2. Retry unnecessarily
  3. Build follow-up reasoning on a false premise

Reproduction

  1. Run any exec command in a turn that gets aborted or crashes mid-session-write
  2. On the next turn, the history replay synthesizes isError: true for that tool call
  3. Model treats the successful (but unrecorded) exec as a failure

Proposed Fix

Change the synthesized placeholder to signal ambiguity rather than failure:

result.push({
  role: "toolResult",
  toolCallId: toolCall.id,
  toolName: toolCall.name,
  content: [{ 
    type: "text", 
    text: "Tool result not recorded — status unknown. Verify state before retrying." 
  }],
  isError: false,  // ambiguous, not failed
  timestamp: Date.now()
});

This forces the model to verify state rather than assume failure, which is the correct behavior when the actual outcome is unknown.

Additional Recommendation

Add a health check to doctor --fix that detects tool calls with synthesized missing results and surfaces them as warnings, so users can identify sessions where result persistence failed.

Environment

  • OpenClaw: 2026.4.14
  • Affected file: dist/anthropic-vertex-stream-YyrYtvts.js line ~4532
  • Trigger: message history replay when toolResult records missing from storage

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:data-lossCan lose, corrupt, or silently drop user/session/config data.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions