Skip to content

Bug: Heartbeat tool_use blocks leak into parent session transcript, corrupting Anthropic sessions #41329

Description

@BooRy95

Description

When a heartbeat agent runs on a non-Anthropic model (e.g. Grok 4.1 Fast via OpenRouter) and produces an alert that gets delivered to the parent session channel, the heartbeat's tool_use blocks leak into the parent session's transcript without their corresponding tool_result blocks. The next Anthropic API call for that session then fails with:

messages.N: tool_use ids were found without tool_result blocks immediately after: callXXXXXXXX, ...

This creates a permanent corruption loop — every subsequent message to the session fails with the same error until /new or /reset is used.

Root Cause (Hypothesis)

The heartbeat runs as an embedded session and makes tool calls (e.g. sessions_list, exec) to perform monitoring. When the heartbeat produces a non-HEARTBEAT_OK response (an alert), the delivery mechanism appears to serialize the heartbeat's assistant turn — including its tool_use content blocks — into the parent session's JSONL transcript, but without the corresponding tool_result entries.

The existing repairToolUseResultPairing in transcript hygiene should synthesize missing results for Anthropic sessions, but it does not appear to catch tool calls injected via heartbeat delivery.

Additional factor: When the heartbeat model is non-Anthropic (OpenRouter/Grok), that model's transcript hygiene path has NO tool result pairing repair (only image sanitization). If the heartbeat's own session cleanup is model-dependent, orphaned tool calls may be persisted before they reach the parent session.

Steps to Reproduce

  1. Configure an agent with a heartbeat using a non-Anthropic model (e.g. openrouter/x-ai/grok-4.1-fast)
  2. Configure the heartbeat with target: "last" to deliver alerts to the active channel
  3. Have a heartbeat prompt that requires tool calls (e.g. sessions_list for context pressure monitoring)
  4. Wait for the heartbeat to fire and produce an alert (not HEARTBEAT_OK)
  5. Send any message to the parent session
  6. Anthropic rejects with tool_use ids found without tool_result blocks
  7. Session is permanently corrupted — every subsequent message fails

Environment

  • OpenClaw 2026.3.8
  • Parent session model: anthropic/claude-opus-4-6
  • Heartbeat model: openrouter/x-ai/grok-4.1-fast
  • Heartbeat config: every: 30m, target: last, lightContext: true
  • Heartbeat prompt includes sessions_list, exec, and other tool-calling checks

Workaround

Switching the heartbeat model to an Anthropic model (e.g. anthropic/claude-haiku-4-5) appears to resolve the issue, likely because Anthropic's transcript hygiene includes tool result pairing repair.

Expected Behavior

Heartbeat tool calls should never leak into the parent session's transcript. The heartbeat's internal tool_use/tool_result pairs should be contained within its own embedded session context, with only the final text output (the alert message) delivered to the parent session.

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions