
Heartbeat Fix
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 → nullBot running for hours — zero heartbeat messages receivedHeartbeat worked once, then stopped foreverSome bots deliver, others don't — same configYour bots aren't dead. OpenClaw is eating their messages before they reach you.
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.
These aren't misconfigurations. They're bugs in OpenClaw's heartbeat pipeline that silently suppress messages at five independent points.
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.
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.
Queue collision
Before firing a heartbeat, OpenClaw checks: "is anything else 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.
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.
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.
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: 30mShorter = more proof-of-life messages but more API calls. Longer = fewer messages but lower cost.
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.
Five bugs — technical detail
HEARTBEAT_OK Suppression
The 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.
Cron Classification Entanglement
Cron 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.
Queue Gate Collision
Gate 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). Example: heartbeat at 31m with crons at 10m → collisions only every 310 minutes (0.3% of ticks).
Duplicate Detection
Bots 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. No errors are 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.
Active Session Drop
resolveActiveRunQueueAction() 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 cannot be prevented. Expected delivery ceiling: ~93%.
The 7-Gate Suppression Pipeline
| Gate | Function | What kills your heartbeat | Our fix |
|---|---|---|---|
| 1 | resolveActiveRunQueueAction() | Bot is busy → heartbeat dropped | None (hardcoded) |
| 2 | normalizeStreamingText() | HEARTBEAT_OK stripped mid-stream | ackMaxChars: 0 |
| 3 | stripHeartbeatToken() | Response too short after strip | ackMaxChars: 0 |
| 4 | buildReplyPayloads() | HEARTBEAT_OK in non-heartbeat payload | ackMaxChars: 0 |
| 5 | normalizeReplyPayload() | Another HEARTBEAT_OK strip pass | ackMaxChars: 0 |
| 6 | isRenderablePayload() | Empty text after normalization | Prompt ensures content |
| 7 | shouldSuppressMessagingToolReplies() | Bot used messaging tool | Documented limitation |
What we're asking OpenClaw to fix
- 1Default prompt biased toward silence
- 2Shared classification pipeline for cron and heartbeat
- 3Queue gate with no heartbeat priority
- 4Dedup filter on a monitoring system
- 5Active 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.
Keep exploring
More free AI tools and guides from Don't Sleep On AI

What Is Clawdbot?
Your own AI assistant running 24/7 on your hardware. No subscriptions, no rate limits — just an AI that actually works for you.
VERSION 2.3.1 — JUST RELEASEDThe Claw Loop
Let Clawdbot and Claude Code build, test, and fix code on their own — while you sleep. This is what autonomous AI development looks like.
UPDATED — v1.1Claw Fix
Drop in a broken app. Claw Fix audits for bugs, writes the fixes, browser-tests every user flow, and ships — no hand-holding required.