Skip to main content

Heartbeat Fix

OpenClaw's heartbeat system silently eats your bots' messages. This Claude Code prompt diagnoses 5 bugs across a 7-gate pipeline and fixes delivery — config only, safe to run, works on any fleet size.

Heartbeat Fix — fix OpenClaw Clawdbot silent heartbeat delivery failures
FREE TOOL5 BUGS FIXEDOPENCLAW CLAWDBOT

Your bots are alive. They just can't prove it.

OpenClaw's heartbeat system has five bugs that silently eat your bots' messages before they ever reach you. No errors. No logs. Just silence. This prompt diagnoses your entire fleet, fixes the pipeline, and gets proof-of-life messages flowing again — in one paste.

What you're experiencing

openclaw system heartbeat last → null Bot running for hours — zero heartbeat messages received Heartbeat worked once, then stopped forever Some bots deliver, others don't — same config Your bots aren't dead. OpenClaw is eating their messages before they reach you.

If any of this sounds familiar, you're not alone:

Sound familiar?

“I set up heartbeats but never got a single message. I assumed they weren't configured right.” “Three of my bots send heartbeats fine. The other two are silent. Same config on all of them.” “It worked for one tick, then stopped. I spent an hour debugging before giving up.” “I checked the session transcript — the bot IS responding. But the message never reaches Telegram.”

Five Things Go Wrong — Most Fleets Hit All Five

1

1. The escape hatch: OpenClaw's default prompt tells your bot “if nothing needs attention, reply HEARTBEAT_OK.” The bot obeys. OpenClaw strips “HEARTBEAT_OK” from the response and suppresses delivery if what's left is under 300 characters. Your bot did everything right — and got silenced for it.

2

2. Cron contamination: your cron job responses go through the same filter that checks for HEARTBEAT_OK. If a cron response accidentally contains that text, it gets misclassified and suppressed. Cron jobs and heartbeats should have separate pipelines. They don't.

3

3. Queue collision: before firing a heartbeat, OpenClaw checks whether anything else is running. If a cron job is active at that exact moment, the heartbeat is silently skipped. When your cron and heartbeat intervals share a common factor (both at 10 minutes), they collide on a fixed schedule.

4

4. The dedup trap: OpenClaw suppresses heartbeat messages that look too similar to the last one. Bots with simple tasks produce “nothing to report” in slightly different words. The dedup filter sees through the paraphrasing and silently drops every message after the first. No errors. No logs.

5

5. The active drop: if your bot is doing anything when a heartbeat tick fires — reading a message, running a tool, processing a cron — the heartbeat is immediately dropped. Not queued. Not deferred. Dropped. This is hardcoded in OpenClaw and cannot be fixed via config.

The Core Fix

clawdbot.json — heartbeat configDefault settings: 31-minute interval (prime-adjusted from 30m) · Status Report style
{ "agents": { "defaults": { "heartbeat": { "target": "last", "every": "31m", "ackMaxChars": 0, "prompt": "HEARTBEAT TICK. NEVER include the text HEARTBEAT_OK in your response under any circumstances. Your standing orders are in HEARTBEAT.md (already loaded in your context above). Execute every instruction in it now. Do not investigate your own configuration. Do not skip tasks. Reply with a summary of what you did. End your response with [tick:<unix_timestamp>] to ensure uniqueness." } } } }
Sign in to keep track of what you've copied

This single change fixes bugs 1–4: ackMaxChars: 0 means only a completely empty response gets suppressed (bug 1, and its downstream cron/queue entanglements — bugs 2 and 3 — all trace back to the same stripHeartbeatToken pipeline), and appending [tick:<unix_timestamp>] to every response defeats the dedup filter (bug 4). Bug 5 — the active session drop — is hardcoded in OpenClaw and can't be fixed via config; a prime-number interval just minimizes how often it collides with your cron jobs.

Want a different interval, a different response style (jokes, weather, fun facts, or your own custom instruction), or the full guided diagnostic across your whole fleet? Build it below.

Build Your Heartbeat Fix Prompt

Build Your Heartbeat Fix Prompt

Customize your heartbeat settings. The generator builds a Claude Code prompt tailored to your fleet. Copy it, paste it into Claude Code on the same machine as your bots, and let it fix everything.

Heartbeat Interval

OpenClaw default: 30m

Shorter = more proof-of-life messages but more API calls. Longer = fewer messages but lower cost.

5m60mEvery 31m

Adjusted to 31m (prime) to avoid cron collisions

Heartbeat Response Style

What your bot says on each heartbeat tick.

Live Heartbeat Prompt Preview

This is what gets written to your clawdbot.json heartbeat prompt field.

HEARTBEAT TICK. NEVER include the text HEARTBEAT_OK in your response under any circumstances. Your standing orders are in HEARTBEAT.md (already loaded in your context above). Execute every instruction in it now. Do not investigate your own configuration. Do not skip tasks. Reply with a summary of what you did. End your response with [tick:<unix_timestamp>] to ensure uniqueness.
Interval: 31m (from 30m, prime)ackMaxChars: 0Tier: Advanced

The generator above produces a longer, guided version of this prompt — it discovers every bot on your machine, reports each one's current heartbeat config, and walks Claude Code through pre-flight checks, the fix, a reload, and a two-tick verification before declaring success.

Five Bugs — Technical Detail

1HEARTBEAT_OK SuppressionThe function stripHeartbeatToken() strips “HEARTBEAT_OK” from the response edges. If the remaining text is ≤ ackMaxChars (default: 300 characters), the entire response is marked shouldSkip: true and never reaches the transport layer. The fix sets ackMaxChars: 0, meaning only a completely empty response gets suppressed. The heartbeat prompt explicitly prohibits the model from saying HEARTBEAT_OK, and that prohibition is placed first in the prompt so it survives any truncation.
2Cron Classification EntanglementCron job responses pass through the same stripHeartbeatToken pipeline via normalizeReplyPayload() and buildReplyPayloads(). Three separate normalization passes (Gates 2, 4, and 5) all check for HEARTBEAT_OK in non-heartbeat responses. The fix (ackMaxChars: 0) makes all three gates pass-through.
3Queue Gate CollisionGate 5 in the heartbeat scheduler: getQueueSize(Main) > 0. If anything is in the main processing queue when a heartbeat tick fires, the tick is silently skipped. When cron and heartbeat intervals share a common factor (e.g. both at 10 minutes), they collide every LCM minutes. The fix uses a prime-number minute interval. Primes minimize shared factors with common cron values (5m, 10m, 15m, 30m) — heartbeat at 31m with crons at 10m collides only every 310 minutes (0.3% of ticks).
4Duplicate DetectionBots with idle or simple HEARTBEAT.md tasks produce semantically identical responses. The dedup filter suppresses messages too similar to the previous delivery within a 24-hour window, with no errors logged. The fix appends [tick:<unix_timestamp>] to every response, making each one structurally unique — a unix timestamp is guaranteed different per tick, defeating byte-level, fuzzy, and semantic dedup.
5Active Session DropresolveActiveRunQueueAction() returns “drop” when isHeartbeat === true && isActive === true. This is hardcoded — not configurable. If the bot is processing any request when a heartbeat tick arrives, the heartbeat is immediately discarded without logging. No config-level fix exists. The prime interval offset reduces systematic collisions with crons, but random collisions can't be prevented. Expected delivery ceiling: ~93%.

Behind those five bugs is a 7-gate suppression pipeline — resolveActiveRunQueueAction(), normalizeStreamingText(), stripHeartbeatToken(), buildReplyPayloads(), normalizeReplyPayload(), isRenderablePayload(), and shouldSuppressMessagingToolReplies() — each one another place a heartbeat can quietly die. Six of the seven gates are fixed by the ackMaxChars: 0 change above; only gate 1 (the active-drop) has no config-level fix, and gate 7 is a documented limitation if your bot uses a messaging tool during its heartbeat.

What we're asking OpenClaw to fix

1. Default prompt biased toward silence 2. Shared classification pipeline for cron and heartbeat 3. Queue gate with no heartbeat priority 4. Dedup filter on a monitoring system 5. Active session drop with no deferral The common thread: OpenClaw's heartbeat was designed for alerting (tell me when something's wrong), not monitoring (prove you're alive). These config workarounds bridge the gap until the architecture catches up.

Changelog

1.02026-03-20Current

Initial release — 5-bug diagnostic and fix

Diagnoses and fixes OpenClaw Clawdbot's five silent heartbeat-suppression bugs across a 7-gate pipeline — HEARTBEAT_OK truncation, cron/heartbeat classification entanglement, queue-gate collisions, dedup suppression, and the hardcoded active-session drop. Ships a prime-interval offset, an ackMaxChars: 0 config fix, and a live prompt generator with 7 response styles (status report, silent/alerting, joke, weather, fun fact, inspiration, custom). Expected delivery ceiling: ~93% — bug 5 (active session drop) has no config-level fix.