openclaw - 💡(How to fix) Fix [Bug]: Delivery-only lanes (zero real turns + delivery-mirror only) bounce replyResolver preflight indefinitely — 2026.5.22

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…

Error Message

[agent/embedded] [compaction] skipping — no real conversation messages (sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>) [diagnostic] message dispatch completed: channel=telegram sessionId=unknown sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A> source=replyResolver outcome=error duration=37396ms error="Error: Preflight compaction required but failed: no real conversation messages" [diagnostic] message processed: channel=telegram chatId=telegram:<chat>:topic:<topic-A> messageId=<id> sessionId=unknown sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A> outcome=error duration=37420ms error="Error: Preflight compaction required but failed: no real conversation messages"

Root Cause

Delivery-only lanes are an ordinary pattern in OpenClaw deployments — any project channel that receives scheduled-job output without expecting interactive replies fits the shape. Two were observed wedged in this incident; a quick audit of the local registry suggests several more lanes in the same shape on this host. Each one is a latent wedge waiting for any inbound to arrive (manual reply, auto-resolver, mention, anything). The 30–37 s bounce ties up the lane during each failed attempt and produces a steady stream of outcome=error diagnostics that obscure unrelated failures in the same journal window.

Fix Action

Fix / Workaround

[agent/embedded] [compaction] skipping — no real conversation messages
  (sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>)
[diagnostic] message dispatch completed: channel=telegram sessionId=unknown
  sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>
  source=replyResolver outcome=error duration=37396ms
  error="Error: Preflight compaction required but failed: no real conversation messages"
[diagnostic] message processed: channel=telegram chatId=telegram:<chat>:topic:<topic-A>
  messageId=<id> sessionId=unknown
  sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>
  outcome=error duration=37420ms
  error="Error: Preflight compaction required but failed: no real conversation messages"

Note: every bounce records sessionId=unknown, even though the registry binding for the lane key DOES exist and has a valid sessionId. The dispatch chain is reading "no resolvable session" out of the same registry entry the binding points at.

Code Example

[agent/embedded] [compaction] skipping — no real conversation messages
  (sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>)
[diagnostic] message dispatch completed: channel=telegram sessionId=unknown
  sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>
  source=replyResolver outcome=error duration=37396ms
  error="Error: Preflight compaction required but failed: no real conversation messages"
[diagnostic] message processed: channel=telegram chatId=telegram:<chat>:topic:<topic-A>
  messageId=<id> sessionId=unknown
  sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>
  outcome=error duration=37420ms
  error="Error: Preflight compaction required but failed: no real conversation messages"

---

30780 ms
32929 ms
33500 ms
35037 ms
37396 ms
37420 ms

---

chatType, channel, groupId, displayName, origin, sessionId,
updatedAt, sessionStartedAt, route, deliveryContext,
lastChannel, lastThreadId, sessionFile

---

contextTokens, model, status, lastInteractionAt, totalTokens, inputTokens,
outputTokens, cacheRead, cacheWrite, totalTokensFresh, lastAccountId,
runtimeMs, endedAt, claudeCliSessionId, cliSessionBindings, cliSessionIds,
skillsSnapshot, systemPromptReport, systemSent, abortedLastRun,
authProfileOverride, authProfileOverrideCompactionCount,
authProfileOverrideSource, modelProvider, lastTo, subject

---

Lane A:  1 session record + 1 assistant/delivery-mirror
Lane B:  1 session record + 3 assistant/delivery-mirror

Across both:
  - 0 records with message.role == "user"
  - 0 records with message.role == "assistant" AND model != "delivery-mirror"
RAW_BUFFERClick to expand / collapse

Environment

  • OpenClaw: 2026.5.22
  • Channel: Telegram (group + forum-topic routing)
  • Agent: single agents.list[main], model claude-opus-4-7
  • Host: single-VPS Linux deployment, no container

TL;DR

Two Telegram topic lanes used as delivery-only outbound channels for scheduled jobs were wedged on 2026-05-29 evening with the compaction-guard fingerprint and remained wedged for ~22 h until a manual scoped registry-key delete recovered them. The lanes' bound transcripts contained zero message.role=="user" records and zero non-mirror assistant turns — only a session-bootstrap record plus 1–3 delivery-mirror records from the scheduled jobs that had written to them. Their registry entries were never-promoted stubs (updatedAt − sessionStartedAt < 1 s), with contextTokens, model, status, and lastInteractionAt absent. Inbound reply attempts hit preflight and threw Preflight compaction required but failed: no real conversation messages, with measured per-bounce durations of 30.78 s – 37.40 s across the recorded bounces.

The wedge is durable: the lane stays in this state until the registry binding is deleted. --fix-missing is not applicable (the bound sessionFile exists on disk; it just has no real conversation in it).

The bounce loop

Verbatim journal lines (sessionKey/topic IDs redacted):

[agent/embedded] [compaction] skipping — no real conversation messages
  (sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>)
[diagnostic] message dispatch completed: channel=telegram sessionId=unknown
  sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>
  source=replyResolver outcome=error duration=37396ms
  error="Error: Preflight compaction required but failed: no real conversation messages"
[diagnostic] message processed: channel=telegram chatId=telegram:<chat>:topic:<topic-A>
  messageId=<id> sessionId=unknown
  sessionKey=agent:main:telegram:group:<chat>:topic:<topic-A>
  outcome=error duration=37420ms
  error="Error: Preflight compaction required but failed: no real conversation messages"

Note: every bounce records sessionId=unknown, even though the registry binding for the lane key DOES exist and has a valid sessionId. The dispatch chain is reading "no resolvable session" out of the same registry entry the binding points at.

Measured bounce durations

From journalctl --user -u openclaw-gateway over the affected lanes (not estimated):

30780 ms
32929 ms
33500 ms
35037 ms
37396 ms
37420 ms

Each bounce occupies the lane for ~30–37 s of replyResolver work before erroring out, after which the next inbound attempt restarts the cycle.

Affected-lane registry shape (never-promoted stub)

Per-lane registry entry shape on these two stuck lanes (full key list of one of them):

chatType, channel, groupId, displayName, origin, sessionId,
updatedAt, sessionStartedAt, route, deliveryContext,
lastChannel, lastThreadId, sessionFile

Fields absent (key not present, not null) on both stubs:

contextTokens, model, status, lastInteractionAt, totalTokens, inputTokens,
outputTokens, cacheRead, cacheWrite, totalTokensFresh, lastAccountId,
runtimeMs, endedAt, claudeCliSessionId, cliSessionBindings, cliSessionIds,
skillsSnapshot, systemPromptReport, systemSent, abortedLastRun,
authProfileOverride, authProfileOverrideCompactionCount,
authProfileOverrideSource, modelProvider, lastTo, subject

Promoted lanes carry the full ~40-key shape; these stubs carry ~13. The two values that are present (updatedAt, sessionStartedAt) diverge by < 1 second:

LanesessionStartedAtupdatedAtΔ (ms)
A17801103846331780110385217584
B17801111751661780111175850684

For comparison, the lane Δ for any lane that has handled real traffic in this registry is at least 6 orders of magnitude larger (updatedAt − sessionStartedAt ≥ 10⁵ ms).

Transcript shape

Both bound sessionFile jsonl files contain only the bootstrap + mirror writes from scheduled outbound jobs. Record count, by type / message.role / message.model:

Lane A:  1 session record + 1 assistant/delivery-mirror
Lane B:  1 session record + 3 assistant/delivery-mirror

Across both:
  - 0 records with message.role == "user"
  - 0 records with message.role == "assistant" AND model != "delivery-mirror"

Mirror writes continued to land in these files for hours after preflight started bouncing, because the scheduled-job outbound path is mirror-only and does not run through replyResolver. So the wedge silently grows the transcript file with more zero-information mirror stubs while every reply attempt fails.

Proposed change (observed-symptom framing, not an upstream-code claim)

A lane whose bound transcript contains zero message.role=="user" records AND zero non-mirror assistant messages is by construction a delivery-only lane — there is nothing for the compaction-guard to compact. Two viable preflight behaviors that both unwedge the case:

  1. Skip preflight on inbound to such a lane: let the new user inbound be the first real turn, which the guard then has something to operate on next time.
  2. Short-circuit preflight to a no-op rather than throw: log a one-line "transcript has nothing to compact, proceeding" and let the inbound continue to a fresh assistant turn that populates the transcript with real content.

Either form removes the 30–37 s bounce loop, and the lane self-heals on the first real inbound.

A complementary cleanup: at the session-promotion path, never-promoted stubs (Δ < 1 s, no lastInteractionAt) probably should not be the target of an inbound at all — the inbound should mint or rebind onto a fresh sessionId rather than reuse a stub that the gateway has never promoted past spawn.

This is an observed-symptom proposal, not a claim about where the bug lives. I have not traced the throw site in the upstream source; the right fix could be in replyResolver, in the compaction-guard preflight itself, in the session-promotion path, or in some combination.

Why this matters

Delivery-only lanes are an ordinary pattern in OpenClaw deployments — any project channel that receives scheduled-job output without expecting interactive replies fits the shape. Two were observed wedged in this incident; a quick audit of the local registry suggests several more lanes in the same shape on this host. Each one is a latent wedge waiting for any inbound to arrive (manual reply, auto-resolver, mention, anything). The 30–37 s bounce ties up the lane during each failed attempt and produces a steady stream of outcome=error diagnostics that obscure unrelated failures in the same journal window.

Recovery procedure currently in use (for context, not part of the ask)

  • Scoped per-key jq del(."<sessionKey>") of the wedged binding from sessions.json, with cp -p backup and count-delta assertion. No gateway restart — the next inbound mints fresh.
  • The registry-wide openclaw sessions cleanup --fix-missing tool is not used here: it is unscoped (acts on every missing-transcript binding) and these lanes are present-transcript wedges anyway, not missing-transcript wedges. Mixing the two would be incorrect even where it might appear to work.
  • Codified locally in a small bash + jq tool that scans the gateway journal for the fingerprint, classifies each candidate against the registry + transcript shape, and clears only those that meet a conservative conjunction (zero real turns + ≥1 mirror + (at-ceiling OR (never-promoted stub AND bounce-age ≥ 30 min)), excluding any lane whose status == "running" or lastInteractionAt is within the last 30 min). The conservatism is necessary because the same shape can also be a freshly-spawned but legitimate lane that hasn't taken its first turn yet — without an age gate, an aggressive scanner would clear lanes the operator is about to use.

Cross-refs

  • #87740 — compaction-guard wedge family (session-boundary safeguards).
  • Local memory: architecture-doc notes on compaction-guard wedge, three entry points; Path 1 (missing transcript) vs Path 2 (present but poisoned) distinction. This incident is a sub-variant of Path 2 where contextTokens never reached the ceiling because the lane was never promoted.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: Delivery-only lanes (zero real turns + delivery-mirror only) bounce replyResolver preflight indefinitely — 2026.5.22