Heartbeats that don't execute are one of the most frustrating OpenClaw experiences because the system appears to be working โ€” the heartbeat fires โ€” but your agent never actually does anything. The skip system exists by design, but the reasons aren't always obvious.

Here's every skip reason you might encounter, what it means, and what (if anything) to do about it.

Skip Reasons Reference

NO_REPLY

Agent replied with nothing

Your HEARTBEAT.md instructions resulted in the agent deciding there was nothing to do, and it replied with the special NO_REPLY token. OpenClaw suppresses the announce (notification) and skips the cycle cleanly.

โœ“ This is correct behavior. No action needed unless you expected something to run.

If you want to confirm your heartbeat actually ran and just had nothing to do, check the heartbeat log file in your workspace.

ANNOUNCE_SKIP

Agent suppressed its own output

The agent completed a turn but its last visible reply was exactly ANNOUNCE_SKIP. OpenClaw treats this as an intentional signal to suppress the notification โ€” the agent did something but decided the result didn't warrant a ping.

โœ“ Expected behavior if you've instructed your agent to avoid spammy notifications.

To see what ran, check your heartbeat-log.md in the workspace โ€” the agent should still be logging its actions there.

requests-in-flight

Previous turn still running

The heartbeat scheduler tried to fire but an existing agent turn was still in progress. OpenClaw doesn't run overlapping turns โ€” it skips and retries after 1 second. If the in-flight turn finishes before the next scheduled heartbeat, everything proceeds normally.

โš  Action needed if this is frequent: Your heartbeat interval is too short relative to how long your agent takes to complete a turn. Increase the heartbeat interval or reduce the scope of work per heartbeat cycle.

// In your heartbeat config โ€” increase interval
"heartbeat": {
  "intervalMinutes": 60  // increase from default 30
}
llm-busy (local models)

Local model already running inference

Specific to setups running local models via Ollama. If a conversation turn is already using the local model when the heartbeat fires, the heartbeat skips rather than queueing two inference jobs simultaneously. This is the heartbeat-cron collision avoidance behavior.

โš  Action if frequent: Use a different model for heartbeat vs interactive sessions, or increase the heartbeat interval to reduce collision probability.

// Configure heartbeat to use a different model
"heartbeat": {
  "model": "ollama/qwen3.5:9b",  // local model for heartbeats
  "intervalMinutes": 60
}
// Keep interactive sessions on your primary model
gateway-offline

Gateway not running when heartbeat fired

The heartbeat scheduler fired but couldn't reach the gateway. Usually means the gateway crashed or was manually stopped. The skip reason logs the timestamp so you can correlate with gateway logs.

โš  Action: Set up the gateway as a systemd service so it auto-restarts on crash. See our auto-start guide for the systemd config.

rate-limited

API provider returned rate limit error

The heartbeat ran but the model API returned a 429 rate limit response. OpenClaw retries with backoff. If you're seeing this frequently, you're hitting provider rate limits โ€” usually token-per-minute limits rather than request limits.

โš  Action: Reduce heartbeat frequency, switch to a lower-tier model for heartbeat tasks, or increase your API tier. Sonnet has higher rate limits than Opus.

context-compaction-required

Context too long โ€” compaction running

The heartbeat session's context window was nearly full. OpenClaw triggered an automatic compaction (summarization of older context) before allowing new turns. The heartbeat is deferred until compaction completes.

โœ“ Usually resolves automatically. If this is recurring, your heartbeat sessions are accumulating too much history.

Fix: Add a HEARTBEAT.md instruction to periodically summarize and clear old context:

# In HEARTBEAT.md
## Context Management
- At the start of each cycle, check if this session is getting long
- If context feels heavy, summarize key decisions to memory.md and start fresh

How to See Skip Reasons in Your Logs

# Recent heartbeat activity with skip reasons
openclaw logs --filter heartbeat --since 24h

# Or check the gateway log directly
openclaw gateway logs | grep -i "skip\|heartbeat"

Heartbeat Never Running At All?

If your heartbeat isn't firing at all (not skipping โ€” just absent), different diagnosis:

The simplest debug flow: Set your HEARTBEAT.md to reply with exactly one line: "Heartbeat at [time]: HEARTBEAT_OK". If that appears in your logs, heartbeats are running. If not, the issue is in the scheduling layer, not your instructions.