Summary
OpenClaw 2026.4.10 appears to have multiple heartbeat-related regressions in isolated mode:
- scheduled heartbeat cadence does not continue after an initial post-reload pulse
openclaw system heartbeat last reports in-memory heartbeat events that can be triggered by exec-event, so it can mislabel async exec completions as heartbeats
lightContext: true is wired through isolated cron/heartbeat paths but does not produce light behavior in practice
memory/heartbeat-state.json does not appear to be written by the shipped runtime at all
These combine to make isolated cheap heartbeat mode appear enabled while leaving no trustworthy receipt trail that cadence, cheapness, or state writing are actually working.
Environment
- OpenClaw:
2026.4.10
- Install: Homebrew /
/opt/homebrew/lib/node_modules/openclaw
- Host: macOS arm64
- Observed locally in a live install with heartbeat config on disk:
heartbeat.session: "heartbeat"
heartbeat.isolatedSession: true
heartbeat.lightContext: true
heartbeat.target: "none"
agents.defaults.embeddedPi.executionContract: "strict-agentic"
Verified behavior
1) Scheduled heartbeat cadence appears broken in isolated mode
After config reload, gateway log shows one heartbeat pulse but no follow-up 15-minute cadence ticks in the next windows.
Observed log lines in ~/.openclaw/logs/gateway.log:
2026-04-11T21:50:27.905-05:00 [reload] config change detected; evaluating reload (agents.defaults.heartbeat.session, agents.defaults.heartbeat.isolatedSession, agents.defaults.embeddedPi)
2026-04-11T21:50:27.951-05:00 [reload] config hot reload applied (agents.defaults.heartbeat.session, agents.defaults.heartbeat.isolatedSession)
2026-04-11T21:50:38.627-05:00 [heartbeat] started
2026-04-11T22:11:27.640-05:00 [gateway] cron: job created
2026-04-11T22:59:42.804-05:00 [gateway] cron: job created
No 22:05, 22:20, 22:35, or 22:50 heartbeat start appears in that window.
Expected:
- once enabled, heartbeat should continue on its configured interval and leave explicit start/skip/fail evidence for each due window
Actual:
- only the initial post-reload pulse appears
- no later cadence evidence is emitted
2) openclaw system heartbeat last is reading a misleading in-memory event surface
I traced this in the shipped dist/ build:
server.impl-95X_lxU6.js exposes last-heartbeat by returning getLastHeartbeatEvent()
heartbeat-visibility-vJxEaSRp.js stores a single in-memory singleton event
heartbeat-wake-D_DqW-eD.js explicitly recognizes exec-event as a heartbeat wake reason
Relevant code path:
server.impl-95X_lxU6.js
"last-heartbeat": ({ respond }) => { respond(true, getLastHeartbeatEvent(), void 0); }
heartbeat-visibility-vJxEaSRp.js
heartbeat-wake-D_DqW-eD.js
resolveHeartbeatReasonKind(reason) includes exec-event
In practice, openclaw system heartbeat last returned JSON like:
{
"ts": 1775963228786,
"status": "ok-empty",
"reason": "exec-event",
"durationMs": 6445,
"silent": true,
"indicatorType": "ok"
}
That was then easy to misread as a real scheduled heartbeat tick, even though it was not trustworthy evidence of cadence.
Expected:
system heartbeat last should report durable heartbeat receipts for actual scheduled/manual heartbeat runs, clearly separated from generic exec-completion-triggered wakeups
Actual:
- the command returns the last in-memory heartbeat event object, which can carry
reason: "exec-event"
3) lightContext: true is wired but not behaviorally light
I verified that lightContext is threaded through both cron and heartbeat code paths:
- cron normalization preserves
payload.lightContext
- cron execution maps it to
bootstrapContextMode: "lightweight"
- heartbeat runner also maps
heartbeat.lightContext === true to bootstrapContextMode: "lightweight"
Relevant files:
openclaw-tools-Co0S582U.js
server.impl-95X_lxU6.js
heartbeat-runner-CUN7Cvbo.js
However, actual isolated verification runs configured with lightContext: true were still heavy:
Verification cron run (~/.openclaw/cron/runs/1e67e3f4-f3d7-48e8-9ed4-f7926a8ea9ec.jsonl):
input_tokens: 37375
output_tokens: 2535
total_tokens: 31308
durationMs: 87775
A tiny ping / Pong. session near the same window also showed unexpectedly high usage inside its transcript:
input: 12280
output: 39
cacheRead: 2560
totalTokens: 14879
- cost about
$0.0319
So while the flag is passed through, the observed runtime behavior is not remotely “cheap”.
Expected:
lightContext: true on isolated cron/heartbeat paths should materially reduce bootstrap/context cost
Actual:
- lightweight mode appears wired but not effective enough to support cheap watchdog use cases
4) memory/heartbeat-state.json appears unwired in the shipped runtime
The workspace file remained stale during multiple heartbeat-adjacent tests:
memory/heartbeat-state.json mtime stayed at 2026-04-11 12:21:42
I also searched the shipped dist/ build for:
heartbeat-state.json
lastHeartbeatAt
and found no runtime references.
Expected:
- runtime should write/update a durable heartbeat state file if that file is part of the intended heartbeat workflow
Actual:
- I could not find any shipped runtime reference that writes
memory/heartbeat-state.json
- this suggests the writer path may be absent from 2026.4.10, not merely failing at runtime
Impact
- cheap isolated heartbeat mode cannot currently be trusted as working
- cadence may silently stop after reload/startup
- the verification CLI can mislead operators by surfacing
exec-event wake objects as “last heartbeat”
- the state file contract appears unimplemented in the shipped build
lightContext may give false confidence that cron/heartbeat probes are cheap when they are not
Suggested fixes
- Add a durable heartbeat receipt record for every actual heartbeat run with:
- trigger reason
- sessionKey
- session id
- isolated true/false
- start/end timestamps
- model
- token usage
- final outcome
- Make
openclaw system heartbeat last read that durable receipt surface, not a generic in-memory singleton event
- Separate
exec-event wakeups from true scheduled heartbeat receipts in the CLI/UI
- Emit explicit scheduler decision logs for each due window:
- started / skipped / suppressed / failed, with reason
- Audit why
bootstrapContextMode: "lightweight" still produces large token loads in isolated cron runs
- Either wire
memory/heartbeat-state.json in runtime or remove/document the expectation if it is not meant to be runtime-owned
Acceptance criteria
- after enabling isolated heartbeat, at least two consecutive due windows produce explicit start/skip/fail evidence in logs or receipts
openclaw system heartbeat last cannot report an exec-event as the canonical “last heartbeat” result
- isolated cron/heartbeat runs with
lightContext: true show materially reduced input/bootstrap tokens versus current behavior
memory/heartbeat-state.json is either written by runtime on real heartbeat runs or officially removed from the expected workflow/docs
Summary
OpenClaw 2026.4.10 appears to have multiple heartbeat-related regressions in isolated mode:
openclaw system heartbeat lastreports in-memory heartbeat events that can be triggered byexec-event, so it can mislabel async exec completions as heartbeatslightContext: trueis wired through isolated cron/heartbeat paths but does not produce light behavior in practicememory/heartbeat-state.jsondoes not appear to be written by the shipped runtime at allThese combine to make isolated cheap heartbeat mode appear enabled while leaving no trustworthy receipt trail that cadence, cheapness, or state writing are actually working.
Environment
2026.4.10/opt/homebrew/lib/node_modules/openclawheartbeat.session: "heartbeat"heartbeat.isolatedSession: trueheartbeat.lightContext: trueheartbeat.target: "none"agents.defaults.embeddedPi.executionContract: "strict-agentic"Verified behavior
1) Scheduled heartbeat cadence appears broken in isolated mode
After config reload, gateway log shows one heartbeat pulse but no follow-up 15-minute cadence ticks in the next windows.
Observed log lines in
~/.openclaw/logs/gateway.log:No
22:05,22:20,22:35, or22:50heartbeat start appears in that window.Expected:
Actual:
2)
openclaw system heartbeat lastis reading a misleading in-memory event surfaceI traced this in the shipped
dist/build:server.impl-95X_lxU6.jsexposeslast-heartbeatby returninggetLastHeartbeatEvent()heartbeat-visibility-vJxEaSRp.jsstores a single in-memory singleton eventheartbeat-wake-D_DqW-eD.jsexplicitly recognizesexec-eventas a heartbeat wake reasonRelevant code path:
server.impl-95X_lxU6.js"last-heartbeat": ({ respond }) => { respond(true, getLastHeartbeatEvent(), void 0); }heartbeat-visibility-vJxEaSRp.jslastHeartbeatheartbeat-wake-D_DqW-eD.jsresolveHeartbeatReasonKind(reason)includesexec-eventIn practice,
openclaw system heartbeat lastreturned JSON like:{ "ts": 1775963228786, "status": "ok-empty", "reason": "exec-event", "durationMs": 6445, "silent": true, "indicatorType": "ok" }That was then easy to misread as a real scheduled heartbeat tick, even though it was not trustworthy evidence of cadence.
Expected:
system heartbeat lastshould report durable heartbeat receipts for actual scheduled/manual heartbeat runs, clearly separated from generic exec-completion-triggered wakeupsActual:
reason: "exec-event"3)
lightContext: trueis wired but not behaviorally lightI verified that
lightContextis threaded through both cron and heartbeat code paths:payload.lightContextbootstrapContextMode: "lightweight"heartbeat.lightContext === truetobootstrapContextMode: "lightweight"Relevant files:
openclaw-tools-Co0S582U.jsserver.impl-95X_lxU6.jsheartbeat-runner-CUN7Cvbo.jsHowever, actual isolated verification runs configured with
lightContext: truewere still heavy:Verification cron run (
~/.openclaw/cron/runs/1e67e3f4-f3d7-48e8-9ed4-f7926a8ea9ec.jsonl):input_tokens: 37375output_tokens: 2535total_tokens: 31308durationMs: 87775A tiny
ping/Pong.session near the same window also showed unexpectedly high usage inside its transcript:input: 12280output: 39cacheRead: 2560totalTokens: 14879$0.0319So while the flag is passed through, the observed runtime behavior is not remotely “cheap”.
Expected:
lightContext: trueon isolated cron/heartbeat paths should materially reduce bootstrap/context costActual:
4)
memory/heartbeat-state.jsonappears unwired in the shipped runtimeThe workspace file remained stale during multiple heartbeat-adjacent tests:
memory/heartbeat-state.jsonmtime stayed at2026-04-11 12:21:42I also searched the shipped
dist/build for:heartbeat-state.jsonlastHeartbeatAtand found no runtime references.
Expected:
Actual:
memory/heartbeat-state.jsonImpact
exec-eventwake objects as “last heartbeat”lightContextmay give false confidence that cron/heartbeat probes are cheap when they are notSuggested fixes
openclaw system heartbeat lastread that durable receipt surface, not a generic in-memory singleton eventexec-eventwakeups from true scheduled heartbeat receipts in the CLI/UIbootstrapContextMode: "lightweight"still produces large token loads in isolated cron runsmemory/heartbeat-state.jsonin runtime or remove/document the expectation if it is not meant to be runtime-ownedAcceptance criteria
openclaw system heartbeat lastcannot report anexec-eventas the canonical “last heartbeat” resultlightContext: trueshow materially reduced input/bootstrap tokens versus current behaviormemory/heartbeat-state.jsonis either written by runtime on real heartbeat runs or officially removed from the expected workflow/docs