openclaw - ✅(Solved) Fix Bug: CLI backend session reset every turn due to per-message metadata in extraSystemPromptHash [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#70100Fetched 2026-04-23 07:29:15
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
referenced ×3closed ×1cross-referenced ×1

CLI backend sessions are reset on every message turn, causing complete loss of conversation context. The root cause is that extraSystemPromptHash includes per-message dynamic content (timestamp, message_id, sender metadata) from buildInboundMetaSystemPrompt(), which changes on every inbound message. This causes resolveCliSessionReuse() to return invalidatedReason: "system-prompt" every turn, discarding the CLI session and all conversation history.

Root Cause

In src/agents/prepare.ts, the extraSystemPrompt is assembled from:

const extraSystemPromptParts = [
    buildInboundMetaSystemPrompt(...),  // ← contains timestamp, message_id, sender info
    groupChatContext,
    groupIntro,
    groupSystemPrompt,
    buildExecOverridePromptHint(...)
].filter(Boolean);

This extraSystemPrompt is then hashed:

const extraSystemPromptHash = hashCliSessionText(extraSystemPrompt);

And checked in resolveCliSessionReuse():

if (normalizeOptionalString(binding?.extraSystemPromptHash) !== currentExtraSystemPromptHash) 
    return { invalidatedReason: "system-prompt" };

Since buildInboundMetaSystemPrompt includes the current message timestamp, the hash changes on every turn, triggering a session reset every time.

Fix Action

Fixed

PR fix notes

PR #70122: fix(cli-session): only hash static extraSystemPrompt for session reuse

Description (problem / solution / changelog)

Summary

Fixes #70100

CLI backend sessions are reset on every message turn because extraSystemPromptHash includes per-message dynamic content from buildInboundMetaSystemPrompt() (timestamps, message IDs, sender metadata). This causes resolveCliSessionReuse() to always return invalidatedReason: "system-prompt", discarding conversation context.

Changes

src/auto-reply/reply/get-reply-run.ts

  • Split extraSystemPromptParts assembly into dynamic and static portions
  • Add extraSystemPromptStatic param with only the static parts (groupChatContext, groupIntro, groupSystemPrompt, exec override hints)

src/agents/cli-runner/types.ts

  • Add extraSystemPromptStatic?: string to RunCliAgentParams

src/agents/cli-runner/prepare.ts

  • Use extraSystemPromptStatic for session reuse hashing, falling back to full extraSystemPrompt if static is not provided

Root Cause

The extraSystemPrompt passed to CLI backend preparation includes buildInboundMetaSystemPrompt() output, which contains per-message metadata (timestamp, message_id, sender info) that changes on every inbound message. When hashCliSessionText() hashes this, the resulting hash differs every turn, causing resolveCliSessionReuse() to invalidate the session.

Evidence

Gateway logs show repeated resets:

cli session reset: provider=claude-work-cli reason=system-prompt
cli exec: provider=claude-work-cli model=sonnet promptChars=450

promptChars stays at ~450 (just system prompt length) instead of growing with conversation history, confirming sessions are never reused.

Impact

  • All CLI backend users (claude-cli, claude-work-cli, codex-cli) experience session reset on every message
  • Multi-turn conversation is effectively broken for CLI backends
  • This contradicts the CLI backends documentation which states "Sessions are supported (so follow-up turns stay coherent)"

Testing

  • Verified that with per-message metadata excluded from the hash, CLI session reuse works correctly
  • session reset: reason=system-prompt no longer appears in logs
  • promptChars grows as expected with accumulated conversation history
  • Genuine system prompt changes still trigger resets via auth-epoch changes

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/cli-runner.spawn.test.ts (modified, +16/-0)
  • src/agents/cli-runner.ts (modified, +1/-0)
  • src/agents/cli-runner/prepare.ts (modified, +7/-1)
  • src/agents/cli-runner/types.ts (modified, +2/-0)
  • src/auto-reply/reply/agent-runner-execution.test.ts (modified, +63/-1)
  • src/auto-reply/reply/agent-runner-execution.ts (modified, +1/-0)
  • src/auto-reply/reply/get-reply-run.ts (modified, +13/-0)
  • src/auto-reply/reply/queue/types.ts (modified, +1/-0)
  • src/hooks/message-hook-mappers.test.ts (modified, +1/-0)

Code Example

const extraSystemPromptParts = [
    buildInboundMetaSystemPrompt(...),  // ← contains timestamp, message_id, sender info
    groupChatContext,
    groupIntro,
    groupSystemPrompt,
    buildExecOverridePromptHint(...)
].filter(Boolean);

---

const extraSystemPromptHash = hashCliSessionText(extraSystemPrompt);

---

if (normalizeOptionalString(binding?.extraSystemPromptHash) !== currentExtraSystemPromptHash) 
    return { invalidatedReason: "system-prompt" };

---

cli session reset: provider=claude-work-cli reason=system-prompt
cli exec: provider=claude-work-cli model=sonnet promptChars=450

---

// In prepare.ts:
const staticExtraSystemPromptParts = [
    groupChatContext,
    groupIntro,
    groupSystemPrompt,
    buildExecOverridePromptHint(...)
].filter(Boolean);

const dynamicExtraSystemPromptParts = [
    buildInboundMetaSystemPrompt(...),
].filter(Boolean);

const extraSystemPromptHash = hashCliSessionText(staticExtraSystemPromptParts.join("\n\n"));
const extraSystemPrompt = [...dynamicExtraSystemPromptParts, ...staticExtraSystemPromptParts].join("\n\n");
RAW_BUFFERClick to expand / collapse

Summary

CLI backend sessions are reset on every message turn, causing complete loss of conversation context. The root cause is that extraSystemPromptHash includes per-message dynamic content (timestamp, message_id, sender metadata) from buildInboundMetaSystemPrompt(), which changes on every inbound message. This causes resolveCliSessionReuse() to return invalidatedReason: "system-prompt" every turn, discarding the CLI session and all conversation history.

Impact

  • All CLI backend users (claude-cli, claude-work-cli, codex-cli, etc.) experience session reset on every message
  • The agent only "remembers" the last message, making multi-turn conversation impossible
  • This contradicts the CLI backends documentation which states "Sessions are supported (so follow-up turns stay coherent)"

Root Cause

In src/agents/prepare.ts, the extraSystemPrompt is assembled from:

const extraSystemPromptParts = [
    buildInboundMetaSystemPrompt(...),  // ← contains timestamp, message_id, sender info
    groupChatContext,
    groupIntro,
    groupSystemPrompt,
    buildExecOverridePromptHint(...)
].filter(Boolean);

This extraSystemPrompt is then hashed:

const extraSystemPromptHash = hashCliSessionText(extraSystemPrompt);

And checked in resolveCliSessionReuse():

if (normalizeOptionalString(binding?.extraSystemPromptHash) !== currentExtraSystemPromptHash) 
    return { invalidatedReason: "system-prompt" };

Since buildInboundMetaSystemPrompt includes the current message timestamp, the hash changes on every turn, triggering a session reset every time.

Evidence

Gateway logs show repeated resets:

cli session reset: provider=claude-work-cli reason=system-prompt
cli exec: provider=claude-work-cli model=sonnet promptChars=450

promptChars stays at ~450 (just system prompt length) instead of growing with conversation history, confirming the CLI session is not being reused.

Proposed Fix

Separate extraSystemPrompt into static and dynamic components. Only hash the static portion (group context, group intro, group system prompt) for session reuse validation. The dynamic per-message metadata (timestamps, message IDs) should be passed to the CLI without triggering a session reset.

// In prepare.ts:
const staticExtraSystemPromptParts = [
    groupChatContext,
    groupIntro,
    groupSystemPrompt,
    buildExecOverridePromptHint(...)
].filter(Boolean);

const dynamicExtraSystemPromptParts = [
    buildInboundMetaSystemPrompt(...),
].filter(Boolean);

const extraSystemPromptHash = hashCliSessionText(staticExtraSystemPromptParts.join("\n\n"));
const extraSystemPrompt = [...dynamicExtraSystemPromptParts, ...staticExtraSystemPromptParts].join("\n\n");

Environment

  • OpenClaw version: 2026.4.21
  • CLI backends affected: claude-cli, claude-work-cli (and likely all others)
  • OS: macOS (Darwin 25.3.0 arm64)

extent analysis

TL;DR

Separate the extraSystemPrompt into static and dynamic components to prevent session resets due to changing dynamic content.

Guidance

  • Identify and isolate the dynamic components in buildInboundMetaSystemPrompt() that cause the extraSystemPromptHash to change on every message turn.
  • Modify prepare.ts to hash only the static portion of extraSystemPrompt for session reuse validation, as proposed in the fix.
  • Verify that the extraSystemPromptHash remains consistent across message turns by checking the gateway logs for repeated resets.
  • Test the conversation history to ensure it is being preserved across multiple turns.

Example

const staticExtraSystemPromptParts = [
    groupChatContext,
    groupIntro,
    groupSystemPrompt,
    buildExecOverridePromptHint(...)
].filter(Boolean);

const dynamicExtraSystemPromptParts = [
    buildInboundMetaSystemPrompt(...),
].filter(Boolean);

const extraSystemPromptHash = hashCliSessionText(staticExtraSystemPromptParts.join("\n\n"));
const extraSystemPrompt = [...dynamicExtraSystemPromptParts, ...staticExtraSystemPromptParts].join("\n\n");

Notes

This fix assumes that the dynamic components in buildInboundMetaSystemPrompt() are the primary cause of the session resets. If other factors are contributing to the issue, additional modifications may be necessary.

Recommendation

Apply the proposed workaround by separating the extraSystemPrompt into static and dynamic components, as this directly addresses the identified root cause of the session resets.

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