Skip to content

Heartbeat exec-event prompt drops actual completion payload #66487

@jeades

Description

@jeades

Bug

When a backgrounded exec session completes, the heartbeat fires correctly via maybeNotifyOnExit, but the prompt sent to the heartbeat model says "the result is shown in the system messages above" without actually injecting the exec completion payload into the heartbeat context. The model then correctly reports it cannot see any results, and delivers that confused response to the user.

Root Cause (two-part)

  1. No dedup between process poll and notifyOnExit: When a cron run launches an exec with background: true, yields, and later polls the result with process poll, the manual poll does not clear notifyOnExit or cancel the pending auto-exit wake. So both the cron (via poll) and the heartbeat (via auto-exit event) consume the completion — the cron gets the real output, and the heartbeat gets a bogus prompt.

  2. Heartbeat prompt uses classification only, not actual event content: The heartbeat runner checks pending system events with isExecCompletionEvent, then calls buildExecEventPrompt, which produces a generic template: "An async command you ran earlier has completed. The result is shown in the system messages above." But the actual queued exec event text is never injected into the heartbeat body, making the prompt wrong in-context.

Reproduction

  1. Create a cron that runs openclaw status via exec (which yields as a background process session)
  2. The cron later consumes the result via process poll
  3. The backgrounded exec completes and triggers notifyOnExit
  4. Heartbeat fires on the cron session with the generic async-completion prompt
  5. The heartbeat model sees no actual command output and responds with confusion
  6. If heartbeat.target is "last" and directPolicy is "allow", that confused response is delivered to the user's DM

Evidence

  • Cron session 7aa43c60 launched openclaw status, yielded as process session briny-nexus, then polled the result successfully
  • Heartbeat session 8e88de9f received only the generic prompt at 2026-04-14T05:00:58Z: "An async command you ran earlier has completed. The result is shown in the system messages above."
  • Model responded: "I don't see system messages with results in my current context"
  • A second heartbeat cycle (d9acd5de) repeated the same pattern, with the model getting increasingly defensive across iterations
  • The confused responses were delivered to the user's Telegram DM

Suggested Fix

Either:

  • Option A: Have process poll clear notifyOnExit on the polled session so heartbeat never fires for already-consumed results
  • Option B: Have buildExecEventPrompt inject the actual exec completion event content into the heartbeat context, not just a classification-based template
  • Option C (ideal): Both — dedup via Option A, and fix the prompt via Option B as defense-in-depth

Workaround

Set heartbeat.target to "none" and heartbeat.directPolicy to "block" to prevent heartbeat from delivering to user-facing channels.

Environment

  • OpenClaw v2026.4.12
  • Heartbeat model: claude-haiku-4.5 via GitHub Copilot
  • heartbeat.isolatedSession: true
  • macOS (arm64)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions