Skip to content

fix: prevent double compaction from destroying preserved messages (#26458)#26502

Open
jaden-clovervnd wants to merge 1 commit intoopenclaw:mainfrom
jaden-clovervnd:fix/double-compaction-stale-usage
Open

fix: prevent double compaction from destroying preserved messages (#26458)#26502
jaden-clovervnd wants to merge 1 commit intoopenclaw:mainfrom
jaden-clovervnd:fix/double-compaction-stale-usage

Conversation

@jaden-clovervnd
Copy link
Contributor

@jaden-clovervnd jaden-clovervnd commented Feb 25, 2026

Summary

Fixes #26458 — After a successful safeguard compaction, a second compaction fires immediately due to stale usage.totalTokens from kept assistant messages, destroying all preserved conversation history.

Root Cause

Pi-coding-agent's prompt() method checks _findLastAssistantMessage() before each prompt and calls _checkCompaction() with that message's usage.totalTokens. After compaction, kept assistant messages still carry their pre-compaction usage values (e.g., 184K tokens), which makes shouldCompact() return true even though the session is already compacted to ~1-5K actual tokens. This triggers a second compaction that finds 0 real messages to keep.

Evidence

  • 22 double compaction events across 8 sessions on a real installation
  • 100% reproduction rate: every compaction is followed by a spurious re-compaction
  • Every double compaction preserves 0 conversation messages (complete amnesia)
  • All 22 cases confirmed: the stale totalTokens from kept assistants exceeds the shouldCompact threshold

Changes

1. compaction-safeguard.ts — Cancel compaction with no real messages

Added a guard at the top of session_before_compact that cancels compaction when messagesToSummarize has no real conversation messages (user, assistant, or toolResult). In the double compaction scenario, only custom/compaction entries remain after the first compaction, so this guard prevents the destructive second compaction.

2. attempt.ts — Skip cache-ttl insertion after compaction

When compaction occurs during an attempt, the openclaw.cache-ttl custom entry is no longer appended. Previously, this entry was the only non-compaction entry in the session, which bypassed prepareCompaction()'s existing guard (checking if the last entry is a compaction type).

Tests

Added 3 test cases to compaction-safeguard.test.ts:

  • Cancels compaction when messagesToSummarize is empty
  • Cancels compaction when messagesToSummarize has only custom entries
  • Proceeds normally when real conversation messages exist

Note on upstream fix

The root cause is in pi-coding-agent's prompt()_checkCompaction(), which should either skip the check when the last assistant predates the latest compaction, or use estimateContextTokens() on current messages instead of stale usage.totalTokens. The fixes in this PR are defense-in-depth on the OpenClaw side to prevent the destructive outcome regardless of the upstream behavior.

Greptile Summary

Fixed double compaction bug that destroyed preserved conversation history after successful compaction. The issue occurred when stale usage.totalTokens from kept assistant messages triggered immediate re-compaction, with 22 confirmed incidents across 8 sessions showing 100% reproduction rate.

Two defensive fixes were implemented:

  • Added early-return guard in compaction-safeguard.ts that cancels compaction when no real conversation messages exist to summarize
  • Modified attempt.ts to skip cache-ttl custom entry insertion after compaction, preventing bypass of existing double-compaction guards

Test coverage includes three new test cases validating the guard behavior for empty messages, custom-only entries, and normal conversation flow.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix addresses a critical bug with a well-documented root cause (22 real incidents with 100% reproduction). Both changes are defensive guards that prevent destructive behavior without altering normal compaction flow. Test coverage validates all edge cases, and the code includes clear comments explaining the issue and fix.
  • No files require special attention

Last reviewed commit: 25c7355

(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!

After a successful safeguard compaction, a second compaction could fire
immediately due to stale usage.totalTokens from kept assistant messages.
The pre-compaction check in pi-coding-agent's prompt() finds a kept
assistant with pre-compaction usage (e.g., 184K tokens) and triggers
shouldCompact() even though the session is already compacted to ~1-5K
tokens. This second compaction destroys all messages the first one
preserved, causing complete conversation amnesia.

Two defense-in-depth fixes:

1. compaction-safeguard.ts: Cancel compaction in session_before_compact
   when messagesToSummarize has no real conversation messages (user,
   assistant, or toolResult). In the double compaction scenario, only
   custom/compaction entries remain, so this guard prevents the spurious
   re-compaction from proceeding.

2. attempt.ts: Skip cache-ttl custom entry insertion when compaction
   occurred during the same attempt. The cache-ttl entry was the only
   non-compaction entry after compaction, bypassing prepareCompaction()'s
   existing double-compaction guard (which checks if the last entry is a
   compaction type).

Verified against 22 double compaction events across 8 real sessions
where every instance showed: Comp1 keeps 1-193 messages, then Comp2
immediately destroys all of them (0 messages kept).

Fixes openclaw#26458
@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Feb 25, 2026
@bethington
Copy link

Nice catch on the root cause analysis — the stale usage.totalTokens from kept assistant messages is a subtle bug. The defense-in-depth approach here is smart: even when pi-coding-agent gets fixed upstream, this guard prevents any future stale-usage edge cases from causing amnesia.

The comment in compaction-safeguard.ts is particularly helpful for future maintainers. 👍

@openclaw-barnacle
Copy link

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added the stale Marked as stale due to inactivity label Mar 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Double compaction destroys all preserved messages — stale usage.totalTokens from kept assistant triggers immediate re-compaction

2 participants