Summary
The per-turn agents.defaults.heartbeat.model override is applied as a session-level persistence rather than a scoped single-turn override. After the heartbeat turn completes, the override is not cleared, so subsequent user turns silently run on the heartbeat model instead of the configured primary.
Environment
- OpenClaw: v2026.4.29 (a448042)
- OS: macOS 26.4.1 (arm64)
- Node.js: 25.8.2
- Models: redacted (see Config below; happy to share privately if needed)
Note: this behavior may also exist in earlier versions — 2026.4.29 is just when I confirmed it. Maintainers may want to check git history beyond the 2026.4.29 baseline.
Config (redacted)
{
"agents": {
"defaults": {
"heartbeat": {
"every": "1h",
"model": "<model-B>"
}
},
"list": [{
"id": "main",
"model": {
"primary": "<model-A>",
"fallbacks": ["<model-B>", "<model-C>"]
},
"thinkingDefault": "max"
}]
}
}
Where:
<model-A> is a large-context reasoning model (~1M context window)
<model-B> is a smaller, faster model (~200k context window) — used both as the heartbeat model and the first fallback
<model-C> is a second fallback (~200k context window)
Expected Behavior
Heartbeat fires → uses <model-B> to process the heartbeat turn only → after the turn completes, the session's active model reverts to <model-A>.
Actual Behavior
Heartbeat fires → uses <model-B> → the model override persists, the session stays on <model-B> indefinitely. Context window also drops from ~1M → ~200k. All subsequent user turns run on the wrong model.
Evidence
Before heartbeat (normal state):
🧠 Model: <model-A>
📚 Context: 80k/1.0m (8%)
After heartbeat (bug state — model stuck):
🧠 Model: <model-B>
📚 Context: 78k/200k (39%)
Note: Think: max from the main agent config is still in effect, but the model selection is wrong.
Workaround
session_status({ sessionKey: "current", model: "default" })
This resets the override and restores <model-A>. But it must be done manually after every heartbeat fire.
Impact
Every hourly heartbeat silently downgrades the user's main session from a 1M-context reasoning model to a 200k-context smaller model. The user may not notice until response quality degrades or context gets truncated unexpectedly.
Root Cause Hypothesis
The heartbeat per-turn model override (agents.defaults.heartbeat.model) is applied as a session-level persistence rather than a scoped single-turn override. After the heartbeat turn completes, the override is not cleared.
Steps to Reproduce
- Configure
agents.defaults.heartbeat.model to a different model than agents.list[main].model.primary
- Wait for heartbeat to fire (or trigger via cron)
- Run
session_status — model will show the heartbeat model, not the configured primary
Summary
The per-turn
agents.defaults.heartbeat.modeloverride is applied as a session-level persistence rather than a scoped single-turn override. After the heartbeat turn completes, the override is not cleared, so subsequent user turns silently run on the heartbeat model instead of the configured primary.Environment
Config (redacted)
{ "agents": { "defaults": { "heartbeat": { "every": "1h", "model": "<model-B>" } }, "list": [{ "id": "main", "model": { "primary": "<model-A>", "fallbacks": ["<model-B>", "<model-C>"] }, "thinkingDefault": "max" }] } }Where:
<model-A>is a large-context reasoning model (~1M context window)<model-B>is a smaller, faster model (~200k context window) — used both as the heartbeat model and the first fallback<model-C>is a second fallback (~200k context window)Expected Behavior
Heartbeat fires → uses
<model-B>to process the heartbeat turn only → after the turn completes, the session's active model reverts to<model-A>.Actual Behavior
Heartbeat fires → uses
<model-B>→ the model override persists, the session stays on<model-B>indefinitely. Context window also drops from ~1M → ~200k. All subsequent user turns run on the wrong model.Evidence
Before heartbeat (normal state):
After heartbeat (bug state — model stuck):
Note:
Think: maxfrom the main agent config is still in effect, but the model selection is wrong.Workaround
This resets the override and restores
<model-A>. But it must be done manually after every heartbeat fire.Impact
Every hourly heartbeat silently downgrades the user's main session from a 1M-context reasoning model to a 200k-context smaller model. The user may not notice until response quality degrades or context gets truncated unexpectedly.
Root Cause Hypothesis
The heartbeat per-turn model override (
agents.defaults.heartbeat.model) is applied as a session-level persistence rather than a scoped single-turn override. After the heartbeat turn completes, the override is not cleared.Steps to Reproduce
agents.defaults.heartbeat.modelto a different model thanagents.list[main].model.primarysession_status— model will show the heartbeat model, not the configured primary