openclaw - ✅(Solved) Fix Bug: heartbeat exec-event relay prompt drops the actual system-event body and emits generic fallback text [1 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#66382Fetched 2026-04-15 06:26:22
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Participants
Timeline (top)
commented ×1cross-referenced ×1referenced ×1

When heartbeat wakes for an async exec completion event, it detects that an exec finished, but the heartbeat prompt only says:

An async command you ran earlier has completed...

It does not include the actual queued system-event text such as:

Exec completed (abcd1234, code 0) :: <compact output>

As a result, the model often receives only a completion cue without the real result body and can emit generic fallback text like:

I can’t see the completed command output from here...

This fallback can then be sent to the user.

There is also a second issue that amplifies the problem:

  • heartbeat uses peekSystemEventEntries(...) rather than consuming processed events
  • so the same malformed exec-completion cue can be seen again on later heartbeat ticks

Root Cause

Root cause

Fix Action

Fixed

PR fix notes

PR #66417: fix(heartbeat): embed exec event text in buildExecEventPrompt

Description (problem / solution / changelog)

Bug: heartbeat exec-event relay prompt drops the actual system-event body

Issue: #66382

Root Cause: When heartbeat wakes for an async exec completion event, returned a generic placeholder 'The result is shown in the system messages above' without the actual exec event text. The model received only a completion cue without the real result body, emitting generic fallback text like 'I'.

Fix:

  • now accepts optional parameter
  • When is provided, the actual event content is embedded directly in the prompt
  • passes filtered exec event texts from

Files changed: (+17 lines), (+2 lines)

Tests:

[email protected] test /data/disk/openclaw node scripts/test-projects.mjs -- src/infra/heartbeat-events-filter.test.ts

 RUN  v4.1.2 /data/disk/openclaw

✓  unit-fast  src/infra/heartbeat-events-filter.test.ts (29 tests) 10ms

 Test Files  1 passed (1)  Tests  29 passed (29)  Start at  16:07:36  Duration  215ms (transform 20ms, setup 0ms, import 46ms, tests 10ms, environment 0ms) → ✅ 29 passed

Changed files

  • extensions/qqbot/src/utils/text-parsing.ts (modified, +2/-2)
  • src/infra/heartbeat-events-filter.ts (modified, +18/-1)
  • src/infra/heartbeat-runner.ts (modified, +2/-1)

Code Example

An async command you ran earlier has completed...

---

Exec completed (abcd1234, code 0) :: <compact output>

---

I can’t see the completed command output from here...

---

news_fetcher ok

---

An async command you ran earlier has completed. The result is shown in the system messages above. Please relay the command output to the user in a helpful way. If the command succeeded, share the relevant output. If it failed, explain what went wrong.

---

I can’t see the completed command output from here.

I checked the session state, and there are **no running or recent sessions**, so there’s nothing I can safely relay.

If you paste the output, I’ll summarize it cleanly.

---

const pendingEventEntries = peekSystemEventEntries(session.sessionKey);
const pendingEvents = ...pendingEventEntries.map((event) => event.text);
const hasExecCompletion = pendingEvents.some(isExecCompletionEvent);
...
prompt: hasExecCompletion
  ? buildExecEventPrompt({ deliverToUser: ... })
  : ...

---

"An async command you ran earlier has completed. The result is shown in the system messages above..."

---

enqueueSystemEvent(
  output
    ? `Exec ${status} (${session.id.slice(0, 8)}, ${exitLabel}) :: ${output}`
    : `Exec ${status} (${session.id.slice(0, 8)}, ${exitLabel})`,
  ...
);

---

peekSystemEventEntries(session.sessionKey)

---

drainSystemEventEntries(sessionKey)
RAW_BUFFERClick to expand / collapse

Bug: heartbeat exec-event relay prompt drops the actual system-event body and can emit generic English fallback text

Summary

When heartbeat wakes for an async exec completion event, it detects that an exec finished, but the heartbeat prompt only says:

An async command you ran earlier has completed...

It does not include the actual queued system-event text such as:

Exec completed (abcd1234, code 0) :: <compact output>

As a result, the model often receives only a completion cue without the real result body and can emit generic fallback text like:

I can’t see the completed command output from here...

This fallback can then be sent to the user.

There is also a second issue that amplifies the problem:

  • heartbeat uses peekSystemEventEntries(...) rather than consuming processed events
  • so the same malformed exec-completion cue can be seen again on later heartbeat ticks

Environment

  • OpenClaw: 2026.4.11 (769908e)
  • Node.js: v24.14.1
  • OS: Linux nanli-XPS-8930 6.17.0-20-generic x86_64 Ubuntu 24.04
  • Affected observed path:
    • isolated cron run completion
    • heartbeat wake reason: exec-event
    • delivery channel: Telegram

Concrete reproduction

Observed with job:

  • Name: 新闻原始抓取
  • Job ID: 392e2cfd-abd2-40a2-8aa2-fc05937e372b

1. The isolated cron run itself is correct

Transcript:

  • ~/.openclaw/agents/main/sessions/113b44cf-527c-4e2d-8195-9d1deae42c2a.jsonl

Final assistant text:

news_fetcher ok

Run history:

  • ~/.openclaw/cron/runs/392e2cfd-abd2-40a2-8aa2-fc05937e372b.jsonl

Relevant entry:

  • 2026-04-14 14:05
  • sessionId: 113b44cf-527c-4e2d-8195-9d1deae42c2a
  • summary: news_fetcher ok
  • delivered: false

2. A later heartbeat turn receives only a generic async-completion prompt

Heartbeat transcript:

  • ~/.openclaw/agents/main/sessions/918ac060-3a28-4924-ae48-362ec6b5af05.jsonl

The heartbeat turn receives this user message:

An async command you ran earlier has completed. The result is shown in the system messages above. Please relay the command output to the user in a helpful way. If the command succeeded, share the relevant output. If it failed, explain what went wrong.

But the heartbeat run does not include the real exec completion text in the prompt itself.

3. Heartbeat then emits generic fallback English

Same transcript final assistant text:

I can’t see the completed command output from here.

I checked the session state, and there are **no running or recent sessions**, so there’s nothing I can safely relay.

If you paste the output, I’ll summarize it cleanly.

This is then actually sent by heartbeat.

openclaw system heartbeat last --json showed:

  • status: sent
  • preview: "I can’t see the completed command output from here..."

Root cause

A. Heartbeat detects exec completion, but drops the real event text

File:

  • dist/heartbeat-runner-U2x6TbnN.js

Relevant logic:

const pendingEventEntries = peekSystemEventEntries(session.sessionKey);
const pendingEvents = ...pendingEventEntries.map((event) => event.text);
const hasExecCompletion = pendingEvents.some(isExecCompletionEvent);
...
prompt: hasExecCompletion
  ? buildExecEventPrompt({ deliverToUser: ... })
  : ...

buildExecEventPrompt(...) only returns a generic template:

"An async command you ran earlier has completed. The result is shown in the system messages above..."

It does not include the actual pendingEvents content.

B. The actual exec completion event body does exist upstream

File:

  • dist/bash-tools.exec-runtime-jfecOhNR.js

Background exec completion enqueues:

enqueueSystemEvent(
  output
    ? `Exec ${status} (${session.id.slice(0, 8)}, ${exitLabel}) :: ${output}`
    : `Exec ${status} (${session.id.slice(0, 8)}, ${exitLabel})`,
  ...
);

So the useful text exists, but the heartbeat prompt builder throws it away.

C. Heartbeat only peeks events, it does not consume processed ones

File:

  • dist/heartbeat-runner-U2x6TbnN.js
  • dist/system-events-BwxCjOwe.js

Heartbeat uses:

peekSystemEventEntries(session.sessionKey)

System events are only cleared by:

drainSystemEventEntries(sessionKey)

but heartbeat does not drain processed exec events after successful handling.

That means the same bad exec-completion cue can survive into later heartbeat runs and repeat.

Expected behavior

For exec-event heartbeat handling:

  1. The heartbeat prompt should include the actual exec completion body, not only a generic cue.
  2. After heartbeat successfully handles the exec completion event, the processed event should be consumed so it does not repeat.

Actual behavior

  1. Heartbeat sees only a generic “async command completed” prompt.
  2. Model lacks the real result body.
  3. Model emits generic fallback English.
  4. Heartbeat may send that fallback to the user.
  5. Because the event is only peeked, the same problem can repeat on later ticks.

Impact

  • User-visible English pollution in non-English chats
  • Incorrect relay of async command completions
  • Repeatable noise because processed events are not consumed

Minimal fix direction

  1. In heartbeat exec-event prompt construction, include the actual exec completion event text from pendingEventEntries.
  2. After successful heartbeat handling, consume the processed exec events instead of leaving them queued.

Notes

This issue is distinct from the isolated cron session inheritance bug involving stale lastHeartbeatText carry-over. That other bug affects session-store initialization. This issue is about heartbeat exec-event prompt construction and event consumption.

extent analysis

TL;DR

Modify the heartbeat exec-event prompt construction to include the actual exec completion event text and consume the processed events after successful handling.

Guidance

  • Review the buildExecEventPrompt function to include the actual pendingEvents content, specifically the exec completion event text.
  • Update the heartbeat logic to consume the processed exec events using drainSystemEventEntries after successful handling, instead of only peeking at them with peekSystemEventEntries.
  • Verify that the heartbeat prompt now includes the actual exec completion event text and that the processed events are being consumed correctly.
  • Test the changes with the provided job 新闻原始抓取 (ID: 392e2cfd-abd2-40a2-8aa2-fc05937e372b) to ensure the issue is resolved.

Example

const pendingEventEntries = peekSystemEventEntries(session.sessionKey);
const pendingEvents = pendingEventEntries.map((event) => event.text);
const hasExecCompletion = pendingEvents.some(isExecCompletionEvent);

if (hasExecCompletion) {
  const execCompletionEvent = pendingEvents.find(isExecCompletionEvent);
  prompt = `An async command you ran earlier has completed: ${execCompletionEvent}`;
  // Consume the processed exec events
  drainSystemEventEntries(session.sessionKey);
}

Notes

The provided code snippet is a simplified example and may require modifications to fit the actual implementation. Additionally, this fix assumes that the drainSystemEventEntries function is correctly implemented and consumes the processed events as intended.

Recommendation

Apply the workaround by modifying the buildExecEventPrompt function and updating the heartbeat logic to consume the processed events. This should resolve the issue and prevent the generic fallback English from being sent to the user.

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

For exec-event heartbeat handling:

  1. The heartbeat prompt should include the actual exec completion body, not only a generic cue.
  2. After heartbeat successfully handles the exec completion event, the processed event should be consumed so it does not repeat.

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: heartbeat exec-event relay prompt drops the actual system-event body and emits generic fallback text [1 pull requests, 1 comments, 2 participants]