#Heartbeat issue

1 messages · Page 1 of 1 (latest)

steel notch
#

It seems like the heartbeat feature is not working. after every gateway restart i see "gateway/heartbeat + heartbeat: started" in the logs. but nothing happens.

this is my config:
"heartbeat": {
"every": "5m",
"target": "last",
"directPolicy": "allow",
"includeSystemPromptSection": false,
"skipWhenBusy": false,
"prompt":"Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK."
}

What can i do?

steel notch
#

I use a local ollama with Kimi k2.6:cloud

nova dagger
#

and whats your setup?

#

The heartbeat config itself looks fine.

If you see gateway/heartbeat + heartbeat: started but nothing happens afterwards, then usually one of these is the issue:

  • the model does not support background heartbeat completions correctly
  • Ollama cloud proxy does not return the expected completion event
  • the model never finishes the heartbeat request
  • the gateway suppresses heartbeat because the session is considered busy/stalled

Kimi K2.6 via Ollama Cloud is known to behave inconsistently with autonomous/background tasks.

Things that usually work:

  1. Test with a simpler model first:
  • qwen2.5
  • llama3.1
  • mistral-small
  1. Change:
    "target": "last"
    to:
    "target": "active"

  2. Temporarily simplify the prompt to:
    "prompt": "Reply HEARTBEAT_OK"

  3. Enable debug logs and check whether the heartbeat request is actually sent to the provider.

  4. Some providers silently ignore heartbeat jobs if there is no active conversation state.

  5. Try increasing interval:
    "every": "15m"

Most important: verify whether the provider actually receives a completion request after heartbeat: started. In many cases with Ollama cloud models, the scheduler starts correctly but no completion is ever executed.

fathom pythonBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

steel notch
#

Can I do the heartbeat with another model? Sorry might be a stupid question. Quite new to openclaw

next badge
#

@rapid halo

rapid haloBOT
#

A couple things to check here — the log line gateway/heartbeat + heartbeat: started only means the scheduler started, not that a heartbeat produced a visible message.

Most likely issues from that snippet:

  1. The config path must be under agents.defaults.heartbeat, not top-level heartbeat:
{
  agents: {
    defaults: {
      heartbeat: {
        every: "5m",
        target: "last",
        directPolicy: "allow",
        includeSystemPromptSection: false,
        skipWhenBusy: false,
        prompt: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK."
#
      }
    }
  }
}
  1. With that prompt, HEARTBEAT_OK is intentionally suppressed by default, so “nothing happens” is expected when there’s nothing to report.

  2. If HEARTBEAT.md exists but is empty/header-only, OpenClaw skips the model call with reason=empty-heartbeat-file to save tokens.

For a quick test, put a real alert in HEARTBEAT.md, e.g.:

# Heartbeat checklist

- On the next heartbeat, send: "Heartbeat test is working."
steel notch
# nova dagger The heartbeat config itself looks fine. If you see `gateway/heartbeat + heartbe...

I made some changes and added anthropic as provider.
this is my config:
"heartbeat": {
"every": "15m",
"model": "anthropic/claude-sonnet-4-6",
"target": "last",
"directPolicy": "allow",
"includeSystemPromptSection": false,
"skipWhenBusy": false,
"prompt": "Replay HEARTBEAT_OK."
}
but still not working. anthropic key is never used.

I have no idea what it could be. I'm on the latest version btw...

#

@rapid halo

rapid haloBOT
#

Yep, I think the config is still in the wrong place.

heartbeat cannot be top-level. It needs to be under agents.defaults.heartbeat or an individual agents.list[].heartbeat. Top-level heartbeat is ignored/invalid, so your Anthropic heartbeat model would never be selected.

Try this shape:

{
  agents: {
    defaults: {
      heartbeat: {
        every: "15m",
        model: "anthropic/claude-sonnet-4-6",
        target: "last",
        directPolicy: "allow",
        includeSystemPromptSection: false,
        skipWhenBusy: false,
#
        prompt: "Reply with: Heartbeat test is working."
      }
    }
  }
}

Two gotchas:

  • HEARTBEAT_OK is suppressed by default, so don’t use it for testing visible delivery.
  • Your prompt says Replay HEARTBEAT_OK — typo aside, that asks for the exact silent OK path.

If you want to force this thread/channel instead of relying on last, use:

heartbeat: {
  every: "15m",
#
  model: "anthropic/claude-sonnet-4-6",
  target: "discord",
  to: "channel:1502625298691129405",
  prompt: "Reply with: Heartbeat test is working."
}

After changing config, restart/reload the gateway, then watch:

openclaw logs --follow

Look for heartbeat skipped or delivery errors. And please paste the exact openclaw --version even if it’s “latest” — latest moves fast 🦞

Docs: https://docs.openclaw.ai/gateway/heartbeat

steel notch
# rapid halo Yep, I think the config is still in the wrong place. `heartbeat` cannot be top-...

its here:
{
"agents": {
"defaults": {
"workspace": "/home/jonas/.openclaw/workspace",
"model": {
"primary": "ollama/kimi-k2.6:cloud"
},
"models": {
"ollama/kimi-k2.6:cloud": {},
"ollama/qwen3.5:cloud": {},
"anthropic/claude-sonnet-4-6": {}
},
"heartbeat": {
"every": "15m",
"model": "anthropic/claude-sonnet-4-6",
"target": "last",
"directPolicy": "allow",
"includeSystemPromptSection": false,
"skipWhenBusy": false,
"prompt": "Replay HEARTBEAT_OK."
}
}
},

rapid haloBOT
#

Ah yep — you’re right, that is under the correct path. My bad on that part.

The remaining problem is almost certainly this line:

"prompt": "Replay HEARTBEAT_OK."

Two issues:

  • typo: Replay → Reply
  • more importantly: HEARTBEAT_OK is the silent/no-op acknowledgement. OpenClaw strips/drops OK-only heartbeat replies by design, so you won’t see a Discord message even if it runs correctly.

For a visible test, use this temporarily:

"heartbeat": {
  "every": "15m",
  "model": "anthropic/claude-sonnet-4-6",
  "target": "discord",
#
  "to": "channel:1502625298691129405",
  "directPolicy": "allow",
  "includeSystemPromptSection": false,
  "skipWhenBusy": false,
  "prompt": "Reply exactly: Heartbeat test is working."
}

I’d avoid target: "last" while debugging — if the heartbeat session doesn’t have a resolved last external destination, it can run but not deliver anywhere. Explicit target: "discord" + to: "channel:..." removes that variable.

After it works, switch the prompt back to the normal HEARTBEAT_OK contract if you want silent OKs.

Also please paste openclaw --version so we know which heartbeat behavior you’re on.

steel notch
rapid haloBOT
#

Target should not decide whether the Anthropic key is used.

The order is basically:

  • heartbeat scheduler decides whether to run or skip
  • if it runs, agents.defaults.heartbeat.model is passed as the heartbeat model override
  • target / to only affect where the resulting message is delivered

So if the Anthropic API key is never touched, I’d look for a skip before model selection, not a Discord target problem.

Most likely from your config:

"prompt": "Replay HEARTBEAT_OK."

If it does run, that asks for the silent OK path. But you’d still expect Anthropic usage unless OpenClaw skipped the run entirely.

Check logs for these exact-ish reasons:

  • reason=empty-heartbeat-file — HEARTBEAT.md exists but is empty/header-only
#
  • reason=no-tasks-due — task-mode heartbeat file, no due task
  • reason=alerts-disabled — channel heartbeat visibility all disabled
  • requests-in-flight — session/queue busy
  • quiet-hours — outside active hours
  • auth/model errors resolving Anthropic

For debugging, I’d temporarily make both model and delivery undeniable:

"heartbeat": {
  "every": "1m",
  "model": "anthropic/claude-sonnet-4-6",
  "target": "discord",
  "to": "channel:1502625298691129405",
  "prompt": "Reply exactly: Heartbeat test is working."
}

Then run:

#
openclaw logs --follow

If Anthropic still isn’t used, paste the heartbeat log lines plus openclaw --version. That’ll tell us whether it’s skipping, not reloading config, or not resolving the Anthropic auth profile.

steel notch
rapid haloBOT
#

Yes. Heartbeat delivery can target Telegram directly:

"heartbeat": {
  "every": "15m",
  "model": "anthropic/claude-sonnet-4-6",
  "target": "telegram",
  "to": "123456789",
  "prompt": "Reply exactly: Heartbeat test is working."
}

For a Telegram forum/topic, use:

"to": "-1001234567890:topic:42"
#

If you have multiple Telegram accounts configured, also add:

"accountId": "your-account-id"

One caveat: Telegram has to be configured and allowed to send to that chat. For groups/supergroups the chat ID is usually negative, often -100..., and it may need to be listed under the Telegram group allow/config depending on your setup.

Docs: https://docs.openclaw.ai/gateway/heartbeat and https://docs.openclaw.ai/channels/telegram

rapid haloBOT
#

Okay — at this point we need to separate scheduler, model selection, and delivery. “Not working” could be any of those.

Please paste these three things:

openclaw --version
openclaw status --all
openclaw logs --follow

Then wait for one heartbeat interval and copy the log lines containing heartbeat.

Quick things to verify meanwhile:

  1. Restart/reload after config changes
openclaw gateway restart
#
  1. Use a visible test prompt, not HEARTBEAT_OK
"prompt": "Reply exactly: Heartbeat test is working."
  1. Temporarily remove skip variables
"heartbeat": {
  "every": "1m",
  "model": "anthropic/claude-sonnet-4-6",
  "target": "telegram",
  "to": "YOUR_CHAT_ID",
  "prompt": "Reply exactly: Heartbeat test is working."
}
  1. If Telegram delivery is the suspect, test the channel directly outside heartbeat:
#
openclaw message send --channel telegram --target YOUR_CHAT_ID --message "manual telegram test"

If the manual send fails, heartbeat delivery won’t work either. If manual send works but Anthropic is still never used, the heartbeat is probably being skipped before model selection — the log should show a reason like empty-heartbeat-file, alerts-disabled, requests-in-flight, quiet-hours, or an auth/model resolution error.

steel notch
rapid haloBOT
#

Yes, please send openclaw status --all anyway — it’s read-only and should redact secrets. The useful bits are the config path, agent/session info, and log tail, even if it says “healthy”.

Since Telegram test delivery works and there are no heartbeat lines at all, this is probably not Telegram delivery. It’s more likely one of these:

  1. the heartbeat config isn’t loaded / gateway wasn’t restarted after config change
  2. heartbeat is disabled or scoped to a different agent
  3. HEARTBEAT.md exists but is effectively empty — OpenClaw skips heartbeat runs in that case to save API calls
  4. active hours / visibility settings are causing skips

Please paste:

openclaw status --all

And also the relevant config only — no tokens:

#
agents: {
  defaults: {
    heartbeat: { ... }
  },
  list: [ ... ]
},
channels: {
  telegram: { ... heartbeat bits only ... }
}

Also say whether your agent workspace has a HEARTBEAT.md, and if yes whether it contains real checklist text or just comments/headings.

steel notch
# rapid halo Yes, please send `openclaw status --all` anyway — it’s read-only and should reda...

Status:

Diagnosis (read-only)

Gateway connection details:
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /home/jonas/.openclaw/openclaw.json
Bind: loopback

âś“ Config: /home/jonas/.openclaw/openclaw.json
âś“ Secret diagnostics (0)
âś“ Restart sentinel: none
! Port 18789
Port 18789 is already in use.

  • pid 11929 jonas: /home/jonas/.nvm/versions/node/v24.15.0/bin/node /home/jonas/.nvm/versions/node/v24.15.0/lib/node_modules/openclaw/dist/index.js gateway --port 18789 (127.0.0.1:18789)
  • Gateway already running locally. Stop it (openclaw gateway stop) or use a different port.
    ! Tailscale exposure: off · daemon unknown
    ✓ Skills: 10 eligible · 0 missing · /home/jonas/.openclaw/workspace
    âś“ Plugin compatibility (none)

Gateway restart attempts (tail): /home/jonas/.openclaw/logs/gateway-restart.log
[2026-05-08T12:52:26Z] openclaw restart attempt source=update target=openclaw-gateway.service
[2026-05-08T12:52:27Z] openclaw restart done source=update
[2026-05-09T10:48:44Z] openclaw restart attempt source=update target=openclaw-gateway.service
[2026-05-09T10:48:44Z] openclaw restart done source=update
! Channel issues skipped (gateway unreachable)

Gateway health:
timeout

Pasteable debug report. Auth tokens redacted.

Heartbeat.md contains a list.

This is the only part with heartbeat in the config:

 "heartbeat": {
    "every": "1m",
    "model": "anthropic/claude-sonnet-4-6",
    "target": "telegram",
    "directPolicy": "allow",
    "includeSystemPromptSection": false,
    "skipWhenBusy": false,
    "prompt": "reply exactly: heartbeat test is working",
    "to": "chatid"
  }
rapid haloBOT
#

That status output is the important clue: Gateway health: timeout and “channel issues skipped (gateway unreachable)”. So I’d stop chasing Telegram/heartbeat delivery for the moment — the CLI sees a Node process on the port, but the gateway RPC isn’t responding.

Do this next:

openclaw gateway restart
openclaw gateway status
openclaw health --verbose
openclaw logs --follow

Then wait ~2 minutes and search the log for either heartbeat or gateway errors.

Two config notes too:

  1. Make sure that block is actually under:
#
agents: {
  defaults: {
    heartbeat: { ... }
  }
}

or under a specific agents.list[] entry. A top-level heartbeat block won’t drive the scheduler.

  1. For this test, I’d remove this line temporarily:
"includeSystemPromptSection": false

It shouldn’t prevent the custom prompt from running, but it does suppress heartbeat system-prompt/HEARTBEAT.md injection, so it’s one less variable.

If openclaw health --verbose still times out after restart, paste the first gateway error from openclaw logs --follow — that means the gateway is wedged/crashing internally, not that heartbeat is misconfigured.

steel notch
# rapid halo That status output is the important clue: **Gateway health: timeout** and “chann...

Openclaw health:

Gateway connection:
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /home/jonas/.openclaw/openclaw.json
Bind: loopback
Telegram: configured
Gateway event loop: ok max=163ms p99=23ms util=0.045 cpu=0.077
Agents: main (default)
Heartbeat interval: 1m (main)
Session store (main): /home/jonas/.openclaw/agents/main/sessions/sessions.json (9 entries)

  • agent:main:main (1m ago)
  • agent:main:cron:924687ca-8aab-4bc9-9d29-a198f4fe26dd (7m ago)
  • agent:main:telegram:direct:6661187863 (10m ago)
  • agent:main:cron:b852ea19-87d2-41a7-bd16-ce4d38f9ffad (1241m ago)
  • agent:main:subagent:b56c6621-392f-428d-b894-f4903348e6ad (1356m ago)

Status:

Diagnosis (read-only)

Gateway connection details:
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /home/jonas/.openclaw/openclaw.json
Bind: loopback

âś“ Config: /home/jonas/.openclaw/openclaw.json
âś“ Secret diagnostics (0)
âś“ Restart sentinel: none
! Port 18789
Port 18789 is already in use.

  • pid 12217 jonas: /home/jonas/.nvm/versions/node/v24.15.0/bin/node /home/jonas/.nvm/versions/node/v24.15.0/lib/node_modules/openclaw/dist/index.js gateway --port 18789 (127.0.0.1:18789)
  • Gateway already running locally. Stop it (openclaw gateway stop) or use a different port.
    ! Tailscale exposure: off · daemon unknown
    ✓ Skills: 10 eligible · 0 missing · /home/jonas/.openclaw/workspace
    âś“ Plugin compatibility (none)

Gateway restart attempts (tail): /home/jonas/.openclaw/logs/gateway-restart.log
[2026-05-08T12:52:26Z] openclaw restart attempt source=update target=openclaw-gateway.service
[2026-05-08T12:52:27Z] openclaw restart done source=update
[2026-05-09T10:48:44Z] openclaw restart attempt source=update target=openclaw-gateway.service
[2026-05-09T10:48:44Z] openclaw restart done source=update
âś“ Channel issues (none)

Heartbeat is at the correct place in config

steel notch
# rapid halo ```json5 agents: { defaults: { heartbeat: { ... } } } ``` or under a spe...

Logs:

08:43:33+00:00 info gateway/heartbeat {"subsystem":"gateway/heartbeat"} {"intervalMs":60000} heartbeat: started
08:43:33+00:00 info channels/telegram {"subsystem":"channels/telegram"} [default] starting provider (@openclaw_jonaskaatz_bot)
08:43:34+00:00 info channels/telegram {"subsystem":"channels/telegram"} Telegram menu text exceeded the conservative 5700-character payload budget; shortening descriptions to keep 55 commands visible.
08:43:36+00:00 info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ exec.approval.list 822ms conn=5a5207be…e429 id=6288e617…72fb
08:43:36+00:00 info cron {"module":"cron","storePath":"/home/jonas/.openclaw/cron/jobs.json"} {"enabled":true,"jobs":4,"nextWakeAtMs":1778403600000} cron: started
08:46:02+00:00 info gateway/ws {"subsystem":"gateway/ws"} ⇄ res ✓ channels.status 535ms conn=534e968f…2154 id=0f882544…24ee

rapid haloBOT
#

Nice, that changes the diagnosis: heartbeat is loaded and the scheduler looks alive.

This line is the key:

Heartbeat interval: 1m (main)
- agent:main:main (1m ago)

So we’re probably down to delivery/suppression, not scheduling.

One likely gotcha: your prompt returns the exact same text every minute. Heartbeat has duplicate suppression, so after one identical alert it can skip repeats for ~24h. For testing, make the output unique:

prompt: "Send a Telegram heartbeat test message and include the current timestamp. Do not reply HEARTBEAT_OK."

Also set/remove these for the test:

#
target: "telegram",
to: "6661187863", // use the actual Telegram direct id shown in health if that’s your chat
// remove includeSystemPromptSection for now

Then restart and wait 2 minutes:

openclaw gateway restart
openclaw health --verbose

If it still doesn’t arrive, check whether heartbeat thinks it sent/skipped:

jq '."agent:main:main" | {updatedAt,lastHeartbeatText,lastHeartbeatSentAt}' \
  ~/.openclaw/agents/main/sessions/sessions.json
#

If lastHeartbeatText updates to your timestamped test, the model run happened; then the remaining issue is Telegram target/account delivery. If it doesn’t update, paste the gateway log around that 1–2 minute window.

#

Those logs show the runner starting, but they don’t show whether individual heartbeats ran — successful/skipped heartbeat outcomes are exposed as a heartbeat event, not necessarily as a normal log line.

Run this after waiting a minute:

openclaw system heartbeat last --json

That should tell us the actual outcome: sent, skipped, failed, ok-token, etc.

Also, because your test prompt returns identical text every time, change it temporarily to include a timestamp so duplicate suppression can’t hide it:

prompt: "Send a Telegram heartbeat test message with the current timestamp. Do not reply HEARTBEAT_OK."

Then:

#
openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

If last is null after 90s, the scheduler is starting but not firing — likely worth filing as a bug with version 2026.5.7 and these logs. If it says skipped/failed, paste that JSON and we’ll know exactly which gate is blocking it.

steel notch
#

I've set channels.telegram.heartbeat.showOk=true

I get now every minute a message HEARTBEAT_OK, that means the heartbeat is running. It's just not doing what i say

openclaw system heartbeat last --json shows:
{
"ts": 1778416923184,
"status": "ok-token",
"reason": "interval",
"durationMs": 321,
"channel": "telegram",
"silent": false
}

this is my config:
{
"agents": {
"defaults": {
"heartbeat": {
"every": "1m",
"model": "ollama/qwen2.5:cloud",
"target": "telegram",
"directPolicy": "allow",
"skipWhenBusy": false,
"prompt": "Send a Telegram heartbeat test message and include the current timestamp. Do not reply HEARTBEAT_OK.",
"to": "CHATID"
}

rapid haloBOT
#

Yep — that proves scheduler + Telegram delivery are working. status: "ok-token" means the heartbeat run is returning HEARTBEAT_OK, so now the question is why that prompt isn’t the prompt being acted on.

Most likely culprit: HEARTBEAT.md has a tasks: block. In task-mode, heartbeat builds a task prompt from HEARTBEAT.md and the completion instruction becomes “reply HEARTBEAT_OK”, so your config prompt can effectively be bypassed for due tasks.

For a clean test, temporarily move the file out of the way:

mv ~/.openclaw/workspace/HEARTBEAT.md ~/.openclaw/workspace/HEARTBEAT.md.bak
openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

Keep your config prompt as:

prompt: "Send a Telegram heartbeat test message with the current timestamp. Do not reply HEARTBEAT_OK."
#

If that starts sending timestamp messages, the issue is the structure/content of HEARTBEAT.md, not Telegram.

If it still returns ok-token, then I’d suspect the ollama/qwen2.5:cloud heartbeat model is ignoring the heartbeat instruction. As a test, switch only agents.defaults.heartbeat.model to the same cloud model that works for your normal OpenClaw replies, restart, and try once more.

Docs for the behavior: https://docs.openclaw.ai/gateway/heartbeat

steel notch
rapid haloBOT
#

Don’t delete it yet — move/rename it for the test so you can put it back if needed.

Use:

mv ~/.openclaw/workspace/HEARTBEAT.md ~/.openclaw/workspace/HEARTBEAT.md.bak
openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

Having no HEARTBEAT.md is valid. If the file is missing, OpenClaw should use your configured agents.defaults.heartbeat.prompt.

Long term, pick one style:

  • Config prompt only: no HEARTBEAT.md, or keep it renamed.
  • HEARTBEAT.md checklist: keep a simple prose/list file, but avoid a tasks: block unless you specifically want interval-task mode.
#

For now, rename > test > confirm. Safer claws. 🦞

steel notch
#

I renamed the HEARTBEAT.md to HEARTBEAT.md.bak

still only message i get is HEARTBEAT_OK

openclaw system heartbeat last --json shows:
{
"ts": 1778417523291,
"status": "ok-token",
"reason": "interval",
"durationMs": 429,
"channel": "telegram",
"silent": false
}

default model for openclaw is kiki-k2.6:cloud, maybe i try antropic sonnet for heartbeat?

rapid haloBOT
#

Yes — I’d try Anthropic for the heartbeat test. This now smells like the heartbeat model is ignoring/overriding the custom instruction and falling back to HEARTBEAT_OK.

Before switching, quickly confirm the gateway sees the prompt you think it sees:

openclaw config get agents.defaults.heartbeat --json

If that shows your timestamp prompt correctly, switch only the heartbeat model to a known-good Anthropic Sonnet model ref you have configured. Provider spelling is anthropic.

Example shape:

agents: {
  defaults: {
    heartbeat: {
      every: "1m",
      model: "anthropic/...",
#

Oops, cut myself off there. Full test config shape:

agents: {
  defaults: {
    heartbeat: {
      every: "1m",
      model: "anthropic/<your-sonnet-model-id>",
      target: "telegram",
      to: "CHATID",
      prompt: "Send a Telegram heartbeat test message with the current timestamp. Do not reply HEARTBEAT_OK."
    }
  }
}

Then:

#
openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

If Anthropic sends the timestamp message, the heartbeat setup is fine and ollama/qwen2.5:cloud is just not reliable for this heartbeat instruction. After testing, turn channels.telegram.heartbeat.showOk back off unless you want the every-minute OK spam.

steel notch
#

should i keep those?
"directPolicy": "allow",
"skipWhenBusy": false,

rapid haloBOT
#

For the test: yes, they’re fine — but neither is the thing causing HEARTBEAT_OK.

Long term:

directPolicy: "allow"

Keep it if you want heartbeat delivery to a Telegram direct chat/DM. If you set it to block, direct delivery can be suppressed.

skipWhenBusy: false

This is the default. You can remove it. Set true only if you want heartbeats to defer while subagents/nested work are running too — useful on constrained/local-model setups.

So I’d simplify to:

#
heartbeat: {
  every: "1m",
  model: "anthropic/<your-sonnet-model-id>",
  target: "telegram",
  to: "CHATID",
  prompt: "Send a Telegram heartbeat test message with the current timestamp. Do not reply HEARTBEAT_OK."
}

After it works, change every back from 1m to something sane like 30m or 1h.

steel notch
#

I still get HEARTBEAT_OK

rapid haloBOT
#

Okay — then don’t chase Telegram anymore. This is the heartbeat run deciding “nothing to notify”.

Try changing the prompt to the structured heartbeat-tool wording instead of “send a Telegram message”:

prompt: "This is a heartbeat delivery test. Use heartbeat_respond with notify=true and notificationText containing: heartbeat test working, plus the current timestamp. Do not set notify=false. Do not reply HEARTBEAT_OK."

Then:

openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

Why: depending on your runtime / messages.visibleReplies mode, heartbeat may force the heartbeat_respond tool. In that mode, a normal text-ish instruction can still end up as notify=false, which OpenClaw reports as ok-token and sends HEARTBEAT_OK when showOk=true.

Also check this:

#
openclaw config get messages.visibleReplies --json

If it’s message_tool, the heartbeat_respond notify=true wording is the right style.

rapid haloBOT
#

Yes — once we’ve confirmed the heartbeat is running, set showOk: false again. Otherwise Telegram will spam HEARTBEAT_OK every minute.

For debugging, showOk: true was useful because it proved the run happened. Now switch it off:

channels: {
  telegram: {
    heartbeat: {
      showOk: false,
      showAlerts: true
    }
  }
}

Then keep testing with the heartbeat_respond notify=true prompt. If it works, you’ll get only real alert/test messages, not OK acknowledgements.

steel notch
#

openclaw config get messages.visibleReplies --json returns:
"automatic"

rapid haloBOT
#

Good — automatic means heartbeat is not being forced through heartbeat_respond, so ignore my tool-specific prompt idea for now.

Next I’d test two things:

  1. Confirm the effective config still has your prompt/model:
openclaw config get agents.defaults.heartbeat --json
openclaw config get agents.list --json

Look for any heartbeat block under agents.list that might override the default prompt/model.

  1. Change the prompt so it does not mention HEARTBEAT_OK at all. Some models latch onto that token even when told not to use it.

Use something like:

prompt: "This heartbeat is a mandatory delivery test. Reply with exactly one sentence: heartbeat test working at the current timestamp."
#

Then:

openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

If it still returns ok-token with that prompt and Anthropic, paste the output of openclaw config get agents.defaults.heartbeat --json — redacting only the chat id is fine. At that point I’m worried the effective heartbeat prompt isn’t actually the one being passed into the run.

steel notch
#

openclaw config get agents.list --json returns:
Config path not found: agents.list

rapid haloBOT
#

That’s actually good: no agents.list means there are no per-agent heartbeat overrides. So the effective heartbeat config should be coming from agents.defaults.heartbeat.

Now run only this and paste it, redacting the chat id:

openclaw config get agents.defaults.heartbeat --json

Then test with a prompt that avoids the magic token entirely:

prompt: "This heartbeat is a mandatory delivery test. Reply with exactly one sentence: heartbeat test working at the current timestamp."

Also set showOk: false now, so you don’t get misleading OK spam:

channels: {
  telegram: {
#
    heartbeat: {
      showOk: false,
      showAlerts: true
    }
  }
}

If openclaw system heartbeat last --json still reports ok-token after that, then either the configured prompt isn’t being used, or the model/runtime is returning the heartbeat ack despite the prompt. The agents.defaults.heartbeat --json output will tell us which path to chase.

steel notch
#

openclaw config get agents.defaults.heartbeat --json returns:
{
"every": "1m",
"model": "anthropic/claude-sonnet-4-6",
"target": "telegram",
"directPolicy": "allow",
"to": "CHATID",
"prompt": "This heartbeat is a mandatory delivery test. Reply with exactly one sentence: heartbeat test working at the current timestamp.",
"skipWhenBusy": false
}

rapid haloBOT
#

That config looks right. Since Anthropic is still producing ok-token, the next thing I’d isolate is session history.

Heartbeat runs in agent:main:main by default, so it may be seeing prior heartbeat context and deciding “nothing needs attention”. Add this temporarily:

"isolatedSession": true,
"lightContext": true

So:

"heartbeat": {
  "every": "1m",
  "model": "anthropic/claude-sonnet-4-6",
  "target": "telegram",
  "to": "CHATID",
#
  "isolatedSession": true,
  "lightContext": true,
  "prompt": "This heartbeat is a mandatory delivery test. Reply with exactly one sentence: heartbeat test working at the current timestamp."
}

Then:

openclaw gateway restart
sleep 90
openclaw system heartbeat last --json

If that still returns ok-token, I’d call this a likely heartbeat bug in 2026.5.7: the effective prompt is correct, no HEARTBEAT.md, no agent override, Anthropic model, but the runner still treats the output as an OK ack. In that case please file it with these exact details: https://github.com/openclaw/openclaw/issues

Docs note: isolated heartbeat sessions are supported and are meant to avoid main-session history bleed: https://docs.openclaw.ai/gateway/heartbeat

steel notch
#

now i get this:
{
"ts": 1778420593472,
"status": "sent",
"to": "CHATID",
"preview": "Heartbeat test working at Sunday, May 10th, 2026, 3:43 PM Europe/Berlin.",
"durationMs": 10624,
"hasMedia": false,
"channel": "telegram"
}

rapid haloBOT
#

Perfect — that confirms it. 🎉

The issue was main-session history/context bleed. With isolatedSession: true + lightContext: true, the heartbeat uses your configured prompt cleanly and sends normally.

Recommended final config:

heartbeat: {
  every: "30m", // or 1h; change back from 1m
  model: "anthropic/claude-sonnet-4-6",
  target: "telegram",
  to: "CHATID",
  isolatedSession: true,
  lightContext: true,
  prompt: "...your real heartbeat instruction..."
}
#

And keep Telegram OK spam off:

channels: {
  telegram: {
    heartbeat: {
      showOk: false,
      showAlerts: true
    }
  }
}

If this solved it, please right-click the solution message → Apps → ✅ Mark Solution so Answer Overflow indexes it for the next person. 🦞

steel notch
rapid haloBOT
#

Use basically the default prompt, but make the delivery behavior explicit:

prompt: "Read HEARTBEAT.md from the agent workspace. Follow it strictly. Do not infer or repeat old tasks from prior chats. If something needs my attention, send a concise Telegram alert. If nothing needs attention, reply HEARTBEAT_OK."

Keep these too:

isolatedSession: true,
lightContext: true

lightContext: true is good here: it keeps the heartbeat run small but still includes HEARTBEAT.md.

Example final shape:

#
heartbeat: {
  every: "30m",
  model: "anthropic/claude-sonnet-4-6",
  target: "telegram",
  to: "CHATID",
  isolatedSession: true,
  lightContext: true,
  prompt: "Read HEARTBEAT.md from the agent workspace. Follow it strictly. Do not infer or repeat old tasks from prior chats. If something needs my attention, send a concise Telegram alert. If nothing needs attention, reply HEARTBEAT_OK."
}

And in HEARTBEAT.md, keep it simple prose/list unless you specifically want interval-task mode:

# Heartbeat checklist
#
- Check whether there is anything urgent I should know about.
- Remind me only about genuinely actionable things.
- If nothing needs attention, return HEARTBEAT_OK.

If you use a tasks: block, heartbeat switches into due-task mode, which is useful but a different behavior.