Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
When agents.defaults.compaction.mode = "safeguard", certain sessions repeatedly trigger the safeguard's "no real conversation messages" branch and write a 144-char fallback summary (## Decisions\nNo prior history.\n...). The actual compaction LLM call then runs in parallel but is canceled after the 60s COMPACTION_RETRY_AGGREGATE_TIMEOUT_MS, and the runtime falls back to a "pre-compaction snapshot" — meaning the transcript still has full size, but a placeholder compaction marker has been recorded. On the next turn the same session re-enters the same path, creating a loop of 60s-stalled turns with empty summaries.
Switching to mode: "default" makes the symptom go away (no safeguard hook → no empty-fallback writes).
The sessions where this triggers are real working sessions with substantive user and assistant messages plus tool calls. A simulation replay of isRealConversationMessage on the same transcript reports 14/108 "real" messages — well above the threshold the safeguard's if (!hasRealSummarizable && !hasRealTurnPrefix) check should require — so something in the message set passed to the safeguard hook is being seen as "no real conversation" even though the file-on-disk transcript clearly contains real conversation.
This affects:
- Cron-triggered sessions (e.g. daily-report cron at 38K tokens)
- Newly-reset interactive sessions (
/new flow)
- Long DM sessions
I have not yet identified the upstream commit that introduced the regression — containsRealConversationMessages and the 60s aggregate timer existed in 2026.4.23 (still ran fine for me there), so the divergence likely lives in how messagesToSummarize is constructed for the session_before_compact event. Happy to dig further if maintainers point at the right area.
Steps to reproduce
- Run OpenClaw 2026.5.3-1 with
agents.defaults.compaction.mode: "safeguard" and Anthropic-style provider (modelsproxy → claude-opus-4-7 in my case)
- Either:
- Trigger a cron task that does a few
read / exec tool calls (the daily report pipeline I have hits this every morning), OR
- Start a
/new session that does a few rounds of investigation (read files, run exec, etc.)
- Wait until preflight compaction triggers (this happens well before context limit because of
truncateAfterCompaction being false in my config — I am NOT setting maxActiveTranscriptBytes)
- Watch the gateway logs
Expected: compaction summary captures the conversation so far.
Actual: 144-char fallback summary; 60s aggregate timeout; pre-compaction snapshot used; same session re-triggers compaction on the next turn.
Expected behavior
Either:
- Fix root cause — the safeguard hook should agree with the underlying transcript on whether real conversation exists. Real cron / interactive sessions with user prompts + assistant tool-using turns should always summarize, not fall through to "No prior history".
- Surface the gap — if the safeguard intentionally treats some message classes as non-conversation (e.g. all
toolCall blocks with empty assistant text), the fallback summary should at least note "compaction skipped — N messages not classified as conversation" rather than "No prior history", to help operators diagnose.
- Make
default the documented recommendation for users without specific safeguard requirements — currently the schema help text says "Keep 'default' unless you observe aggressive history loss near limit boundaries" but the wizard / docs are silent on the regression risk of safeguard.
Actual behavior
Logs show repeated:
[compaction-safeguard] Compaction safeguard: no real conversation messages to summarize; writing compaction boundary to suppress re-trigger loop.
[agent/embedded] compaction retry aggregate timeout (60000ms): proceeding with pre-compaction state runId=... sessionId=...
[agent/embedded] using pre-compaction snapshot: timed out during compaction runId=... sessionId=...
Recorded compaction entries in transcript files have:
{"type":"compaction","summary":"## Decisions\nNo prior history.\n\n## Open TODOs\nNone.\n\n## Constraints/Rules\nNone.\n\n## Pending user asks\nNone.\n\n## Exact identifiers\nNone captured.","tokensBefore":38085,"fromHook":true}
User-visible impact:
- Each compaction-loop turn adds ~60s of dead time
- API token usage is wasted on the aborted summarization calls
- Session history is not actually compressed — pre-compaction snapshot is restored, so the next turn faces the same pressure
- Cron pipelines that should finish in 5–10 min stretch to 15+ min
OpenClaw version
2026.5.3-1 (commit 2eae30e)
Operating system
Ubuntu 20.04 LTS, kernel 5.10.25, x64
Install method
npm install -g openclaw via tarball (proxy-bound install)
Model
claude-opus-4-7
Provider / routing chain
feishu (DM / cron) → openclaw gateway → model-provider(enterprize) → anthropic-messages
Additional provider/model setup details
- Compaction model NOT explicitly overridden — uses session default
agents.defaults.compaction.mode: "safeguard" (was the trigger; switching to "default" resolved symptoms)
agents.defaults.compaction.timeoutSeconds: not set, so 900s default
agents.defaults.compaction.truncateAfterCompaction: not set (so byte-trigger is disabled, only token threshold is active)
Logs, screenshots, and evidence
Repro count over ~12 hours on 2026-05-06
| Trigger time (HKT) |
Session |
tokensBefore |
Outcome |
| 08:42 |
abf1edff (caixuedan-daily-report cron) |
38085 |
Empty fallback + 60s timeout |
| 11:02 |
75e6e379 (/new interactive) |
76486 |
Empty fallback + 60s timeout |
| 11:31 |
75e6e379 same |
— |
Empty fallback (re-loop) |
| 11:51 |
75e6e379 same |
— |
60s timeout |
| 12:11 |
001e8097 (DM) |
69035 |
Empty fallback + 60s timeout |
| 12:14, 12:19 |
75e6e379 again |
— |
More re-loops |
| 12:20 |
75e6e379 again |
— |
60s timeout |
For comparison, on 2026-05-05 (same install, same config) one compaction at 178K tokens did produce a real ~5KB summary on session 963e608a. So the safeguard path is not 100% broken — it's an as-yet-uncharacterized condition that some sessions hit and others don't.
Sample log excerpt
May 06 08:42:16 ... [compaction-safeguard] Compaction safeguard: no real conversation messages to summarize; writing compaction boundary to suppress re-trigger loop.
May 06 08:43:16 ... [agent/embedded] compaction retry aggregate timeout (60000ms): proceeding with pre-compaction state runId=abf1edff-9629-4b4b-9168-fe4ec4d1c41a sessionId=abf1edff-9629-4b4b-9168-fe4ec4d1c41a
May 06 08:43:16 ... [agent/embedded] using pre-compaction snapshot: timed out during compaction runId=abf1edff-9629-4b4b-9168-fe4ec4d1c41a sessionId=abf1edff-9629-4b4b-9168-fe4ec4d1c41a
Sample empty fallback summary entry from transcript
{
"type": "compaction",
"id": "17c34363",
"parentId": "3aa68b51",
"timestamp": "2026-05-06T00:42:16.544Z",
"summary": "## Decisions\nNo prior history.\n\n## Open TODOs\nNone.\n\n## Constraints/Rules\nNone.\n\n## Pending user asks\nNone.\n\n## Exact identifiers\nNone captured.",
"firstKeptEntryId": "1e91a7ec",
"tokensBefore": 38085,
"fromHook": true
}
Workaround that resolved my issue
"compaction": {
- "mode": "safeguard"
+ "mode": "default"
}
After systemctl --user restart openclaw-gateway no further [compaction-safeguard] log lines appear.
Hypothesis (for maintainer triage)
compaction-successor-transcript-D4FqvNh7.js line 3585 (in 2026.5.3-1 dist):
if (!hasRealSummarizable && !hasRealTurnPrefix) {
log.info("Compaction safeguard: no real conversation messages to summarize; writing compaction boundary to suppress re-trigger loop.");
return { compaction: {
summary: buildStructuredFallbackSummary(preparation.previousSummary),
firstKeptEntryId: preparation.firstKeptEntryId,
tokensBefore: preparation.tokensBefore
} };
}
hasRealSummarizable is computed from stripRuntimeContextCustomMessages(preparation.messagesToSummarize). My guess: preparation.messagesToSummarize in some code paths contains only the slice of messages already split off as "to-summarize" while real conversation messages are in preparation.turnPrefixMessages or a different field, so the OR-check (!hasRealSummarizable && !hasRealTurnPrefix) should be evaluating both, but one or both views may be wrong post-strip.
A repro with [compaction-diag] debug logging enabled would pinpoint which of messagesToSummarize / turnPrefixMessages is empty when the safeguard fires.
Happy to provide the affected *.jsonl transcripts (after redaction) and a longer log window if useful.
Impact and severity
No response
Additional information
No response
Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
When
agents.defaults.compaction.mode = "safeguard", certain sessions repeatedly trigger the safeguard's "no real conversation messages" branch and write a 144-char fallback summary (## Decisions\nNo prior history.\n...). The actual compaction LLM call then runs in parallel but is canceled after the 60sCOMPACTION_RETRY_AGGREGATE_TIMEOUT_MS, and the runtime falls back to a "pre-compaction snapshot" — meaning the transcript still has full size, but a placeholder compaction marker has been recorded. On the next turn the same session re-enters the same path, creating a loop of 60s-stalled turns with empty summaries.Switching to
mode: "default"makes the symptom go away (no safeguard hook → no empty-fallback writes).The sessions where this triggers are real working sessions with substantive
userandassistantmessages plus tool calls. A simulation replay ofisRealConversationMessageon the same transcript reports 14/108 "real" messages — well above the threshold the safeguard'sif (!hasRealSummarizable && !hasRealTurnPrefix)check should require — so something in the message set passed to the safeguard hook is being seen as "no real conversation" even though the file-on-disk transcript clearly contains real conversation.This affects:
/newflow)I have not yet identified the upstream commit that introduced the regression —
containsRealConversationMessagesand the 60s aggregate timer existed in 2026.4.23 (still ran fine for me there), so the divergence likely lives in howmessagesToSummarizeis constructed for thesession_before_compactevent. Happy to dig further if maintainers point at the right area.Steps to reproduce
agents.defaults.compaction.mode: "safeguard"and Anthropic-style provider (modelsproxy → claude-opus-4-7 in my case)read/exectool calls (the daily report pipeline I have hits this every morning), OR/newsession that does a few rounds of investigation (read files, run exec, etc.)truncateAfterCompactionbeing false in my config — I am NOT settingmaxActiveTranscriptBytes)Expected: compaction summary captures the conversation so far.
Actual: 144-char fallback summary; 60s aggregate timeout; pre-compaction snapshot used; same session re-triggers compaction on the next turn.
Expected behavior
Either:
toolCallblocks with empty assistant text), the fallback summary should at least note"compaction skipped — N messages not classified as conversation"rather than"No prior history", to help operators diagnose.defaultthe documented recommendation for users without specific safeguard requirements — currently the schema help text says"Keep 'default' unless you observe aggressive history loss near limit boundaries"but the wizard / docs are silent on the regression risk ofsafeguard.Actual behavior
Logs show repeated:
Recorded
compactionentries in transcript files have:{"type":"compaction","summary":"## Decisions\nNo prior history.\n\n## Open TODOs\nNone.\n\n## Constraints/Rules\nNone.\n\n## Pending user asks\nNone.\n\n## Exact identifiers\nNone captured.","tokensBefore":38085,"fromHook":true}User-visible impact:
OpenClaw version
2026.5.3-1 (commit 2eae30e)
Operating system
Ubuntu 20.04 LTS, kernel 5.10.25, x64
Install method
npm install -g openclawvia tarball (proxy-bound install)Model
claude-opus-4-7
Provider / routing chain
feishu (DM / cron) → openclaw gateway → model-provider(enterprize) → anthropic-messages
Additional provider/model setup details
agents.defaults.compaction.mode: "safeguard"(was the trigger; switching to"default"resolved symptoms)agents.defaults.compaction.timeoutSeconds: not set, so 900s defaultagents.defaults.compaction.truncateAfterCompaction: not set (so byte-trigger is disabled, only token threshold is active)Logs, screenshots, and evidence
Repro count over ~12 hours on 2026-05-06
/newinteractive)For comparison, on 2026-05-05 (same install, same config) one compaction at 178K tokens did produce a real ~5KB summary on session 963e608a. So the safeguard path is not 100% broken — it's an as-yet-uncharacterized condition that some sessions hit and others don't.
Sample log excerpt
Sample empty fallback summary entry from transcript
{ "type": "compaction", "id": "17c34363", "parentId": "3aa68b51", "timestamp": "2026-05-06T00:42:16.544Z", "summary": "## Decisions\nNo prior history.\n\n## Open TODOs\nNone.\n\n## Constraints/Rules\nNone.\n\n## Pending user asks\nNone.\n\n## Exact identifiers\nNone captured.", "firstKeptEntryId": "1e91a7ec", "tokensBefore": 38085, "fromHook": true }Workaround that resolved my issue
"compaction": { - "mode": "safeguard" + "mode": "default" }After
systemctl --user restart openclaw-gatewayno further[compaction-safeguard]log lines appear.Hypothesis (for maintainer triage)
compaction-successor-transcript-D4FqvNh7.jsline 3585 (in 2026.5.3-1 dist):hasRealSummarizableis computed fromstripRuntimeContextCustomMessages(preparation.messagesToSummarize). My guess:preparation.messagesToSummarizein some code paths contains only the slice of messages already split off as "to-summarize" while real conversation messages are inpreparation.turnPrefixMessagesor a different field, so the OR-check(!hasRealSummarizable && !hasRealTurnPrefix)should be evaluating both, but one or both views may be wrong post-strip.A repro with
[compaction-diag]debug logging enabled would pinpoint which ofmessagesToSummarize/turnPrefixMessagesis empty when the safeguard fires.Happy to provide the affected
*.jsonltranscripts (after redaction) and a longer log window if useful.Impact and severity
No response
Additional information
No response