Skip to content

[Bug]: loadCliSessionReseedMessages returns [] without compaction, defeating historyPrompt fallback #79713

@PowerShot

Description

@PowerShot

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

When a Claude CLI session is invalidated (e.g. reason=missing-transcript), the historyPrompt fallback is unreachable for any session that has never been compacted, because loadCliSessionReseedMessages returns [] in that case. Observed in production: 96/96 invalidations over 14 days produced historyPrompt=none and the agent restarted with no prior context.

Steps to reproduce

  1. Start an OpenClaw conversational session (e.g. Discord DM, dmScope: per-channel-peer) on claude-cli runtime.
  2. Have a short exchange (1–3 turns), well below any compaction threshold.
  3. Wait for the Claude CLI live session to close on idle (CLAUDE_LIVE_IDLE_TIMEOUT_MS = 600 * 1e3, i.e. 10 min) OR cause an interruption mid-stream.
  4. Send a new message.
  5. Observe in ~/.openclaw/logs/gateway.log:
    cli session reset: provider=claude-cli reason=missing-transcript
    cli exec: provider=claude-cli ... useResume=false ... reuse=invalidated:missing-transcript historyPrompt=none
    
  6. The agent has no memory of the prior turns.

Expected behavior

When reuse=invalidated:* fires, the openClawHistoryPrompt fallback path is intended to inject prior conversation context (per the existing code in prepare.runtime.../execute.runtime). The agent should restart the CLI session with a "Continue this conversation using the OpenClaw transcript below" preamble (the literal string built by buildCliSessionHistoryPrompt).

Actual behavior

historyPrompt=none is logged on every invalidation event. The fresh CLI session receives only the current user message with no prior context. From the user's perspective, the agent has lost memory.

OpenClaw version

2026.5.6 (c97b9f7)

Operating system

macOS (Darwin 25.3.0, arm64)

Install method

npm install -g openclaw

Model

claude-cli/claude-opus-4-7 (also reproduced with claude-cli/claude-sonnet-4-6 and claude-cli/claude-haiku-4-5)

Provider / routing chain

openclaw → claude-cli → anthropic

Logs, screenshots, and evidence

Code reference — src/agents/cli-runner/session-history.ts (bundled at dist/session-history-DD0rk4PH.js in 2026.5.6):

async function loadCliSessionReseedMessages(params) {
    const entries = await loadCliSessionEntries(params);
    const latestCompactionIndex = entries.findLastIndex(
        (entry) => entry.type === "compaction" && typeof entry.summary === "string"
    );
    if (latestCompactionIndex < 0) return [];   // ← returns empty if never compacted
    // ... compaction-summary + tail-message path
}

Consumed at dist/prepare.runtime-BtdJ-JrR.js ~line 823:

const openClawHistoryPrompt = reusableCliSession.sessionId
    ? void 0
    : buildCliSessionHistoryPrompt({
        messages: await loadCliSessionReseedMessages({...}),
        prompt: preparedPrompt
    });

Since loadCliSessionReseedMessages returns [] for un-compacted sessions, buildCliSessionHistoryPrompt finds no rendered history and returns undefined. The fallback path is silently inert.

Production gateway log breakdown over 14 days:

  • 77 events with reason=missing-transcript
  • 19 events with reason=system-prompt
  • 96/96 logged historyPrompt=none — fallback never produced any prelude.

Impact and severity

  • Affected: any user on a conversational channel (Discord, Telegram, etc.) using the claude-cli provider with sessions short enough to never trigger compaction (in practice, the vast majority of DM-style usage).
  • Severity: Behavior bug, user-visible. The agent loses memory mid-conversation when sessions are invalidated, which happens on idle close (every 10 min of inactivity) and on any system-prompt change (e.g. workspace bootstrap drift, gitStatus updates).
  • Frequency: 77 missing-transcript + 19 system-prompt invalidations in 14 days on a single instance with moderate Discord traffic.
  • Consequence: degraded user experience perceived as "the bot lost context"; manual re-priming needed each time.

Additional information

Suggested fix (untested) — when no compaction is present, fall back to a tail of raw messages:

 async function loadCliSessionReseedMessages(params) {
     const entries = await loadCliSessionEntries(params);
     const latestCompactionIndex = entries.findLastIndex(
         (entry) => entry.type === "compaction" && typeof entry.summary === "string"
     );
-    if (latestCompactionIndex < 0) return [];
+    if (latestCompactionIndex < 0) {
+        // No compaction yet — fall back to last N raw messages so the
+        // historyPrompt path isn't silently inert on short sessions.
+        const tailMessages = entries.flatMap((entry) =>
+            entry.type === "message" ? [entry.message] : []
+        );
+        return tailMessages.slice(-RESEED_TAIL_FALLBACK_MESSAGES);
+    }
     // ... existing compaction path unchanged
 }

A new constant like RESEED_TAIL_FALLBACK_MESSAGES = 30 would keep the prelude bounded; each message is then capped further by maxHistoryChars = 12288 in buildCliSessionHistoryPrompt.

Architectural note (broader gap) — this bug is symptomatic of a wider design gap: backend invalidations (cli session reset reason=missing-transcript, reason=system-prompt) emit only a cliBackendLog.info(...) line, with no plugin hook fired. The before_reset plugin hook is only emitted by performGatewaySessionReset (i.e. user-initiated /new and /reset), not by these backend resets. As a result, plugins cannot observe or react to these silent invalidations.

This gives maintainers two possible fix paths:

  1. Local fix (this issue): make loadCliSessionReseedMessages produce a usable prelude even without prior compaction (the diff above).
  2. Architectural fix (broader): fire a before_reset (or new typed) plugin hook on backend invalidations too, so plugins can implement their own context-recovery strategy. This would also enable observability for operators tracking session health.

The two are independent — fix (1) is enough to resolve the symptom; fix (2) would unlock more patterns for power users without requiring upstream patching.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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