openclaw - ✅(Solved) Fix [Bug]: Pre-compaction memory flush leaks into main session as user messages and causes compaction loop [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#54408Fetched 2026-04-08 01:28:00
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Author
Participants

Fix Action

Temporary workaround

  • abandon the polluted session and start a new one
  • optionally disable agents.defaults.compaction.memoryFlush temporarily

PR fix notes

PR #51421: fix(memory): memoryFlush fires every compaction cycle instead of every other

Description (problem / solution / changelog)

Summary

  • Fixes memoryFlush only firing on alternating compaction cycles
  • One-line behavioral change: stop reassigning memoryFlushCompactionCount to the post-increment value

Root cause

After a memoryFlush during compaction, memoryFlushCompactionCount is reassigned to the post-increment compactionCount (line 536 of agent-runner-memory.ts). This locks both counters to the same value. On the next cycle, the dedup gate at reply-state.ts sees memoryFlushCompactionCount === compactionCount and skips the flush. Only after a regular compaction (without flush) increments compactionCount alone do the values differ again, allowing the next flush.

The result: flush, skip, flush, skip — memoryFlush fires on every other compaction instead of every one.

Fix

Remove the reassignment. memoryFlushCompactionCount stays at the pre-increment value (N). After incrementCompactionCount(), compactionCount becomes N+1. Next cycle, N !== N+1, flush fires. The counter is then updated through the normal session store update path.

-      const nextCount = await incrementCompactionCount({
+      await incrementCompactionCount({
         sessionEntry: activeSessionEntry,
         sessionStore: activeSessionStore,
         sessionKey: params.sessionKey,
         storePath: params.storePath,
       });
-      if (typeof nextCount === "number") {
-        memoryFlushCompactionCount = nextCount;
-      }
+      // Do NOT reassign memoryFlushCompactionCount to the post-increment value.
+      // Keeping it at the pre-increment value ensures the next compaction cycle
+      // sees different counters, allowing memoryFlush to fire every cycle
+      // instead of every other. See #12590.

Prior art

This issue has been reported and patch-attempted multiple times:

  • PR #12760 by @lailoo — same fix approach, closed without merging
  • PR #46513 — closed
  • PR #26145 — related fix, closed
  • @dial481 has been keeping the issue alive through multiple stale-bot cycles

We've been running this fix as a local dist patch since early March 2026 with no issues.

Fixes #12590.

Test plan

  • Local patch running in production since 2026-03-08, no regressions
  • Existing reply-state.test.ts tests for memoryFlushCompactionCount logic pass
  • Change is minimal — removes a reassignment, adds a comment

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]

Changed files

  • src/auto-reply/reply/agent-runner-memory.ts (modified, +6/-5)
  • src/auto-reply/reply/reply-state.test.ts (modified, +31/-0)
RAW_BUFFERClick to expand / collapse

What happened

pre-compaction memory flush messages that should be silent were injected into the main user session as normal user messages.

After that, compaction started treating those internal flush prompts as conversation content, and the session entered a loop where nearly every new input triggered the same flush/compaction behavior.

Expected behavior

The pre-compaction memory flush should remain an internal/silent turn. Users should not see the flush prompt in the chat UI or session transcript. Compaction should continue normally after the flush.

Actual behavior

The following internal prompt appeared directly in the session/chat:

Pre-compaction memory flush. Store durable memories only in memory/YYYY-MM-DD.md ... If nothing to store, reply with NO_REPLY.

Then the session kept hitting compaction with logs like:

Compaction safeguard: no real conversation messages to summarize; writing compaction boundary to suppress re-trigger loop.

Repro notes

  1. Use OpenClaw TUI / main session
  2. Keep chatting until auto-compaction / pre-compaction memory flush triggers
  3. The flush prompt appears in the visible conversation
  4. Continue chatting in the same session
  5. Session gets stuck repeatedly surfacing the internal flush behavior

Environment

  • OpenClaw local gateway
  • TUI main session
  • agents.defaults.compaction.mode = "safeguard"
  • agents.defaults.compaction.memoryFlush.enabled = true

Evidence

The session transcript contains the flush prompt as role: user, repeated multiple times in the same session JSONL.

Gateway logs repeatedly show:

Compaction safeguard: no real conversation messages to summarize; writing compaction boundary to suppress re-trigger loop.

Why this seems like a bug

Docs describe memory flush as a silent turn that should generally resolve to NO_REPLY without being surfaced to the user. In this case, the internal prompt leaked into the visible/main session and then poisoned later compaction.

Temporary workaround

  • abandon the polluted session and start a new one
  • optionally disable agents.defaults.compaction.memoryFlush temporarily

Suggested fix

  • ensure pre-compaction memory flush messages are never persisted into the visible/main transcript as user messages
  • ensure silent/internal turns are excluded from compaction summarization input
  • guard against replay loops when a session already contains leaked internal flush prompts

extent analysis

Fix Plan

To address the issue, we need to ensure that pre-compaction memory flush messages are not persisted as user messages and are excluded from compaction summarization. Here are the steps:

  • Modify the pre-compaction memory flush message handling to mark it as an internal message:
    • Add a flag to the message object, e.g., isInternal: true
  • Update the message persistence logic to exclude internal messages:
    • Check the isInternal flag before writing the message to the transcript
  • Modify the compaction summarization logic to ignore internal messages:
    • Filter out messages with the isInternal flag set to true
  • Implement a safeguard to prevent replay loops when a session contains leaked internal flush prompts:
    • Check for repeated internal flush prompts and skip compaction if detected

Example Code

# Mark pre-compaction memory flush messages as internal
def handle_pre_compaction_flush(message):
    message.isInternal = True
    return message

# Exclude internal messages from persistence
def persist_message(message):
    if not message.isInternal:
        # Write message to transcript
        pass

# Ignore internal messages during compaction summarization
def summarize_compaction(messages):
    filtered_messages = [m for m in messages if not m.isInternal]
    # Summarize filtered messages
    pass

# Prevent replay loops
def check_replay_loop(session):
    if session.contains_leaked_flush_prompt:
        # Skip compaction
        pass

Verification

To verify the fix, test the following scenarios:

  • Trigger a pre-compaction memory flush and verify that the message is not visible in the chat UI or session transcript.
  • Continue chatting in the same session and verify that compaction occurs normally without getting stuck in a loop.
  • Check the session transcript and gateway logs to ensure that the internal flush prompt is not repeated multiple times.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Temporary workaround

  • abandon the polluted session and start a new one
  • optionally disable agents.defaults.compaction.memoryFlush temporarily

Expected behavior

The pre-compaction memory flush should remain an internal/silent turn. Users should not see the flush prompt in the chat UI or session transcript. Compaction should continue normally after the flush.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING