Skip to content

Bug: compaction.notifyUser notices gated behind onBlockReply — async compactions silently notify nothing #88933

@kiagentkronos-cell

Description

@kiagentkronos-cell

First, I want to sincerely apologize for the flood of issues coming from this account. I think OpenClaw is genuinely brilliant, and I want to help make it even better. Thank you to all the maintainers for your outstanding work — this project is exceptional, and reporting bugs is my way of giving back.


Affected version: openclaw@2026.5.27 (running); code present in current dist bundle

Summary

With agents.defaults.compaction.notifyUser: true, the user is supposed to be told when a context compaction happens. But the notice is delivered only through the active turn streaming block-reply callback onBlockReply. The notice function hard-returns when that callback is absent.

Consequence: any compaction that runs outside an active, inbound-driven streaming reply turn — e.g. a proactive maxHistoryShare-triggered compaction, or a compaction inside an announce/embedded run (subagent-completion auto-announce) — has no wired onBlockReply, so sendCompactionNotice returns immediately. No notice is delivered, none is queued, and there is no retry.

Impact

  • notifyUser: true gives a false sense of coverage. The user is notified only when a compaction happens to coincide with a live streaming reply turn; the asynchronous / proactive ones — which are the ones a user would most want flagged — produce nothing.
  • Failure is silent: the early return logs nothing; even the delivery catch only goes to logVerbose (not visible at default log level).
  • It compounds a separate delivery gap (see Related): when a subagent-completion auto-announce is dropped and the surrounding compaction is also unannounced, the user has no signal at all that the conversation state changed underneath them.

Environment

openclaw 2026.5.27 (gateway)
platform Linux aarch64 (NVIDIA GB10 / Spark)
provider openai-completions -> vLLM, Qwen/Qwen3.6-27B-FP8
config agents.defaults.compaction = { notifyUser: true, maxHistoryShare: 0.3, mode, reserveTokensFloor, memoryFlush }
channel WhatsApp direct

Reproduction / observed evidence

Timeline (session bf5ec6e1-..., WhatsApp direct):

  1. 2026-06-01T04:30:52Z assistant produces a reply (in response to an internal subagent-completion event, not a WhatsApp inbound).
  2. 2026-06-01T04:34:22Z a compaction record is written to the session — ~3.5 min after the previous turn finished and ~17 min before the next inbound message.
  3. Gateway log between 04:00-07:00 CEST contains zero auto-compaction start / context overflow detected lines -> this was a proactive / non-overflow compaction.
  4. The user received no compaction notice, despite notifyUser: true.

The compaction ran with no active inbound streaming turn -> no onBlockReply -> sendCompactionNotice early-returned.

Root cause

The notice is bound to the per-turn streaming callback rather than to the channel/session delivery layer. sendCompactionNotice is only reachable via the three call sites inside the streaming reply path (phase: start | end | incomplete), each guarded by shouldNotifyUserAboutCompaction, and all of them depend on params.opts.onBlockReply being present. Compactions triggered outside that path (proactive maxHistoryShare, embedded/announce runs) have no such callback.

Location (dist bundle): dist/reply-turn-admission-BaGuBaDP.js, function sendCompactionNotice and the three else-if call sites. (Source path inferred: src/agents/.../reply-turn-admission.ts.)

Proposed fix

Decouple the compaction notice from onBlockReply so it can be delivered for any compaction on the session:

  • When onBlockReply is unavailable, fall back to a direct channel/session send (the same path used to push subagent auto-announces to the requester session), addressed by sessionKey / channel of the session being compacted.
  • Alternatively, emit the notice through the session delivery queue so it is durable and retried.
  • At minimum: when notifyUser === true and onBlockReply is absent at notice time, log it at a visible level (not logVerbose) so the gap is observable rather than silent.

Related (separate issue, same family)

In the same session, the assistant reply generated at step 1 (a subagent-completion auto-announce) was itself never delivered to WhatsApp and was not found in delivery-queue/failed. That is the previously known subagent->requester announce-delivery drop — but both share the root theme: events not driven by an inbound channel message are not reliably pushed to the channel.

This is distinct from the recently fixed finish_reason: stop + structured tool_calls issue (#88791).

Metadata

Metadata

Assignees

Labels

P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions