openclaw - ✅(Solved) Fix [Bug]: relayable exec completion heartbeat leaks HEARTBEAT_OK as user-visible alert [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#78197Fetched 2026-05-06 06:15:58
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
2
Timeline (top)
closed ×1commented ×1cross-referenced ×1

A heartbeat run triggered by a relayable async exec completion can deliver the raw protocol token HEARTBEAT_OK to the user. HEARTBEAT_OK is a heartbeat no-op/ack token and should be swallowed regardless of whether the heartbeat was triggered by a timer, cron event, or exec completion.

Observed on the shiki-tokyo host on 2026-05-06 (hostname, paths, config, and transcript contents redacted). Reproduced against current main source at 7f71e84248.

Root Cause

A heartbeat run triggered by a relayable async exec completion can deliver the raw protocol token HEARTBEAT_OK to the user. HEARTBEAT_OK is a heartbeat no-op/ack token and should be swallowed regardless of whether the heartbeat was triggered by a timer, cron event, or exec completion.

Observed on the shiki-tokyo host on 2026-05-06 (hostname, paths, config, and transcript contents redacted). Reproduced against current main source at 7f71e84248.

Fix Action

Fix / Workaround

Severity: medium-low but user-visible. It leaks an internal protocol token, creates confusing background notifications, and undermines the intended no-op semantics for heartbeat. The feature should still keep useful background exec completion notifications; disabling tools.exec.notifyOnExit is only a workaround.

PR fix notes

PR #78200: fix: suppress exec heartbeat ack leaks

Description (problem / solution / changelog)

Fixes #78197.

Summary

  • keep pure HEARTBEAT_OK normalized as a protocol ack for relayable exec-completion heartbeat wakes
  • remove the exec-completion fallback that restored stripped ack text as user-visible delivery
  • add a regression test for relayable exec completion returning HEARTBEAT_OK, while preserving real exec summary delivery coverage

Verification

  • git diff --check
  • PATH="/tmp/openclaw-pnpm-shim:$PATH" pnpm exec oxfmt --check src/infra/heartbeat-runner.ts src/infra/heartbeat-runner.ghost-reminder.test.ts
  • attempted PATH="/tmp/openclaw-pnpm-shim:$PATH" node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/heartbeat-runner.ghost-reminder.test.ts — blocked before tests by existing local missing @openclaw/fs-safe/config dependency
  • PATH="/tmp/openclaw-pnpm-shim:$PATH" node scripts/check-changed.mjs passed conflict/changelog/extension wildcard/duplicate lanes; core typecheck blocked by unrelated existing missing @openclaw/fs-safe/* modules and pre-existing strictness diagnostics outside this patch

Changed files

  • src/infra/heartbeat-runner.ghost-reminder.test.ts (modified, +57/-0)
  • src/infra/heartbeat-runner.ts (modified, +4/-17)

Code Example

event = Exec completed (review-run, code 0) :: review-worker spawn finished
model reply = HEARTBEAT_OK
result = {"status":"ran"}
outbound send = {"text":"HEARTBEAT_OK","to":"<redacted-target>"}

---

pnpm test src/infra/heartbeat-runner.ghost-reminder.test.ts -t "metadata-only successful exec completions|includes untrusted exec completion details"
=> 1 file passed, 2 tests passed, 18 skipped

---

2026-05-06T09:13:29+08 Stripped stray HEARTBEAT_OK token <redacted> reply
2026-05-06T09:21:52+08 Stripped stray HEARTBEAT_OK token <redacted> reply
2026-05-06T09:21:57+08 Feishu final delivery preview referenced that HEARTBEAT_OK had been shown to the user and should have been swallowed.
2026-05-06T09:30:09+08 Stripped stray HEARTBEAT_OK token <redacted> reply
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug: wrong user-visible output, no crash.

Beta release blocker

No.

Summary

A heartbeat run triggered by a relayable async exec completion can deliver the raw protocol token HEARTBEAT_OK to the user. HEARTBEAT_OK is a heartbeat no-op/ack token and should be swallowed regardless of whether the heartbeat was triggered by a timer, cron event, or exec completion.

Observed on the shiki-tokyo host on 2026-05-06 (hostname, paths, config, and transcript contents redacted). Reproduced against current main source at 7f71e84248.

Steps to reproduce

  1. Enable heartbeat delivery to a user-visible target, for example target: "last" with directPolicy: "allow".
  2. Queue a relayable exec completion system event, for example Exec completed (review-run, code 0) :: review-worker spawn finished.
  3. Run heartbeat processing for the exec event and have the model/provider return exactly HEARTBEAT_OK.
  4. Observe that outbound delivery receives and sends text: "HEARTBEAT_OK".

Expected behavior

If the final normalized heartbeat response is pure HEARTBEAT_OK, gateway should treat it as a protocol no-op and skip user delivery. Exec completion reminders should still be delivered when the model returns a real summary or warning.

Actual behavior

normalizeHeartbeatReply correctly strips a pure HEARTBEAT_OK reply to shouldSkip=true, but the relayable exec completion branch restores the original reply text and clears the skip flag. That turns the protocol token into ordinary alert text.

Relevant current-source path:

  • src/agents/bash-tools.exec-runtime.ts:318 queues the exec completion event and requests heartbeat for exec-event.
  • src/infra/heartbeat-runner.ts:1040 collects exec completion events.
  • src/infra/heartbeat-runner.ts:1046 computes hasRelayableExecCompletion.
  • src/infra/heartbeat-events-filter.ts:36 treats structured exec completions with output, or failed completions, as relayable.
  • src/auto-reply/heartbeat.ts:147 strips heartbeat ack tokens.
  • src/infra/heartbeat-runner.ts:1689 intentionally prevents skip for exec completion events.
  • src/infra/heartbeat-runner.ts:1693 restores the original reply text when the normalized text is empty.
  • src/infra/heartbeat-runner.ts:1704 excludes relayable exec completions from shouldSkipMain.
  • src/infra/heartbeat-runner.ts:1861 sends normalized.text as an outbound payload.

OpenClaw version

Current-source repro: upstream/main at 7f71e84248.

Observed shiki-tokyo installed version: NOT_ENOUGH_INFO. The fresh logs show npm-global runtime paths and Node v22.22.0, but openclaw was not on the noninteractive SSH PATH for an exact --version check.

Operating system

Source-level repro: macOS development checkout.

Observed host: shiki-tokyo, Ubuntu Linux kernel 5.15.0-164-generic, x86_64.

Install method

Local repro: source checkout with pnpm.

Observed host: appears to be npm-global install from runtime log paths. Exact launch/install method: NOT_ENOUGH_INFO.

Model

Observed host logs: openai-codex/gpt-5.5.

Local deterministic repro: mocked heartbeat model boundary returning exactly HEARTBEAT_OK.

Provider / routing chain

Observed host: OpenClaw gateway -> openai-codex -> gpt-5.5, channel delivery through Feishu.

Local deterministic repro: runHeartbeatOnce with a lightweight test messaging plugin and mocked model response.

Additional provider/model setup details

Observed host heartbeat config, redacted to non-secret fields: target=last, directPolicy=allow, ackMaxChars=300, lightContext=true, skipWhenBusy=true, active hours in Asia/Shanghai.

Logs, screenshots, and evidence

Issue template/rules checked before filing: .github/ISSUE_TEMPLATE/bug_report.yml, .github/ISSUE_TEMPLATE/config.yml, and CONTRIBUTING.md.

Duplicate search was performed for HEARTBEAT_OK exec completion, notifyOnExit heartbeat, and HEARTBEAT_OK. Related issues are listed below, but this issue has a narrower deterministic current-source reproducer.

Local deterministic repro output, redacted:

event = Exec completed (review-run, code 0) :: review-worker spawn finished
model reply = HEARTBEAT_OK
result = {"status":"ran"}
outbound send = {"text":"HEARTBEAT_OK","to":"<redacted-target>"}

Targeted existing tests around adjacent behavior pass but do not cover this regression:

pnpm test src/infra/heartbeat-runner.ghost-reminder.test.ts -t "metadata-only successful exec completions|includes untrusted exec completion details"
=> 1 file passed, 2 tests passed, 18 skipped

Fresh shiki-tokyo log excerpts, redacted:

2026-05-06T09:13:29+08 Stripped stray HEARTBEAT_OK token <redacted> reply
2026-05-06T09:21:52+08 Stripped stray HEARTBEAT_OK token <redacted> reply
2026-05-06T09:21:57+08 Feishu final delivery preview referenced that HEARTBEAT_OK had been shown to the user and should have been swallowed.
2026-05-06T09:30:09+08 Stripped stray HEARTBEAT_OK token <redacted> reply

A plain heartbeat poll control case in the same log family showed assistantTexts:["HEARTBEAT_OK"] with didSendViaMessagingTool:false, which is the expected silent path. The failing path is the relayable exec completion branch above.

Impact and severity

Affected users: anyone with tools.exec.notifyOnExit enabled and heartbeat delivery allowed to a user-visible target.

Severity: medium-low but user-visible. It leaks an internal protocol token, creates confusing background notifications, and undermines the intended no-op semantics for heartbeat. The feature should still keep useful background exec completion notifications; disabling tools.exec.notifyOnExit is only a workaround.

Frequency: deterministic when a relayable exec completion is inspected and the model decides no user notification is needed by returning pure HEARTBEAT_OK.

Suggested fix

Do not restore or deliver original reply text when the normalized heartbeat response is a skip token. In particular, a pure normalized HEARTBEAT_OK should remain shouldSkip=true even when hasRelayableExecCompletion is true.

A regression test should cover: structured relayable exec completion with output + model reply HEARTBEAT_OK -> no outbound send, while the inspected exec event is still consumed/recorded according to the existing event lifecycle.

Related issues

  • #74257: similar symptom around Telegram async exec followup and context overflow, but this reproducer does not require overflow and points to the current relayable exec fallback branch.
  • #73149: broader cron/heartbeat no-op leak class; this issue isolates exec completion heartbeat handling.
  • #75322: adjacent heartbeat exec prompt/no-reply branch, but this issue is specifically deliverable exec completion + pure HEARTBEAT_OK.

extent analysis

TL;DR

The most likely fix is to prevent restoring the original reply text when the normalized heartbeat response is a skip token, such as HEARTBEAT_OK, even when there's a relayable exec completion.

Guidance

  • Review the src/infra/heartbeat-runner.ts file, specifically lines 1693 and 1704, to ensure that the shouldSkip flag is not cleared when the normalized text is empty and hasRelayableExecCompletion is true.
  • Update the logic to maintain shouldSkip=true for pure HEARTBEAT_OK responses, regardless of the presence of relayable exec completions.
  • Add a regression test to cover the scenario where a structured relayable exec completion with output and a model reply of HEARTBEAT_OK results in no outbound send.
  • Verify that the fix does not interfere with the delivery of useful background exec completion notifications.

Example

// src/infra/heartbeat-runner.ts
if (normalizedText === 'HEARTBEAT_OK') {
  shouldSkip = true;
  // Do not restore original reply text here
}

Notes

This fix assumes that the issue is solely related to the handling of HEARTBEAT_OK responses in the context of relayable exec completions. Additional testing and verification may be necessary to ensure that this change does not introduce unintended behavior in other scenarios.

Recommendation

Apply the suggested fix to prevent restoring the original reply text when the normalized heartbeat response is a skip token, and add a regression test to cover the specific scenario described in the issue. This should resolve the medium-low severity issue without disabling the tools.exec.notifyOnExit feature.

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

If the final normalized heartbeat response is pure HEARTBEAT_OK, gateway should treat it as a protocol no-op and skip user delivery. Exec completion reminders should still be delivered when the model returns a real summary or warning.

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]: relayable exec completion heartbeat leaks HEARTBEAT_OK as user-visible alert [1 pull requests, 1 comments, 2 participants]