openclaw - 💡(How to fix) Fix Telegram DM: cliSessionBindings.claude-cli.sessionId not updated after session_expired retry — duplicate cli exec on every turn, mid-conversation amnesia [2 comments, 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#73073Fetched 2026-04-28 06:27:52
View on GitHub
Comments
2
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
commented ×2

In OpenClaw 2026.4.21 / 2026.4.24, the per-channel cliSessionBindings.claude-cli.sessionId for a Telegram-direct session can fossilize on a session id whose ~/.claude/projects/<encoded-cwd>/<id>.jsonl no longer exists. Once that happens, every subsequent turn:

  1. Tries to resume the dead id (executePreparedCliRun(context, context.reusableCliSession.sessionId)dist/cli-runner-z8DwqtjV.js:64).
  2. Fails with FailoverError(reason="session_expired").
  3. Retries fresh (executePreparedCliRun(context, void 0)dist/cli-runner-z8DwqtjV.js:73), which does succeed and creates a new claude-cli JSONL.
  4. Returns successfully — but the new session id is never round-tripped back into cliSessionBindings. Next turn, the same dead id is loaded and the dance repeats.

User-visible symptoms:

  • Two [agent/cli-backend] cli exec: provider=claude-cli model=… promptChars=N lines in gateway.out ~1s apart with identical promptChars for every Telegram message. The pair are the original try and the post-session_expired retry.
  • The agent loses conversational continuity between every two turns — typically presenting as the agent forgetting what the user just said. Personality stays intact (boot files load fresh) but mid-conversation memory is gone.

The bug is severe in the wild because it self-perpetuates: once the binding fossilizes (one bad sessionId persisted, even from a prior gateway/CLI version), there's no path inside the gateway that ever updates it.

Root Cause

The bug is severe in the wild because it self-perpetuates: once the binding fossilizes (one bad sessionId persisted, even from a prior gateway/CLI version), there's no path inside the gateway that ever updates it.

Fix Action

Fix / Workaround

Workaround we shipped

RAW_BUFFERClick to expand / collapse

Summary

In OpenClaw 2026.4.21 / 2026.4.24, the per-channel cliSessionBindings.claude-cli.sessionId for a Telegram-direct session can fossilize on a session id whose ~/.claude/projects/<encoded-cwd>/<id>.jsonl no longer exists. Once that happens, every subsequent turn:

  1. Tries to resume the dead id (executePreparedCliRun(context, context.reusableCliSession.sessionId)dist/cli-runner-z8DwqtjV.js:64).
  2. Fails with FailoverError(reason="session_expired").
  3. Retries fresh (executePreparedCliRun(context, void 0)dist/cli-runner-z8DwqtjV.js:73), which does succeed and creates a new claude-cli JSONL.
  4. Returns successfully — but the new session id is never round-tripped back into cliSessionBindings. Next turn, the same dead id is loaded and the dance repeats.

User-visible symptoms:

  • Two [agent/cli-backend] cli exec: provider=claude-cli model=… promptChars=N lines in gateway.out ~1s apart with identical promptChars for every Telegram message. The pair are the original try and the post-session_expired retry.
  • The agent loses conversational continuity between every two turns — typically presenting as the agent forgetting what the user just said. Personality stays intact (boot files load fresh) but mid-conversation memory is gone.

The bug is severe in the wild because it self-perpetuates: once the binding fossilizes (one bad sessionId persisted, even from a prior gateway/CLI version), there's no path inside the gateway that ever updates it.

Affected versions

Confirmed in [email protected] and [email protected]. The 2026.4.24 changelog adds:

Claude CLI/sessions: classify "No conversation found with session ID" as session_expired so expired CLI-backed conversations clear the stale binding and recover on the next turn. (#65028)

That fixes the classification path so the retry fires reliably. It does not fix the persist-after-retry path, which is the bug here.

Steps to reproduce

  1. Use a claude-cli-backed agent on the Telegram channel.
  2. Either: wait long enough for claude-cli to garbage-collect its ~/.claude/projects/<encoded-cwd>/*.jsonl, or manually delete the JSONL whose id is in cliSessionBindings.claude-cli.sessionId for any agent:main:telegram:direct:* entry of ~/.openclaw/agents/main/sessions/sessions.json.
  3. Send any new Telegram message.
  4. Observe:
    • gateway.out shows two paired cli exec lines for the message.
    • Conversation continuity broken — the agent treats the response as the start of a new conversation.
    • cliSessionBindings.claude-cli.sessionId in sessions.json is still the dead id, even though a new live JSONL now exists in ~/.claude/projects/….

Suspected fix area

In dist/attempt-execution.runtime-BFklk5cT.js:286-326, the runCliWithSession(void 0).then(async (result) => { … setCliSessionBinding(updatedEntry, …, result.meta.agentMeta.cliSessionBinding); … }) path is gated on result.meta.agentMeta?.cliSessionBinding?.sessionId being truthy.

buildCliRunResult (dist/cli-runner-z8DwqtjV.js:46-58) only sets cliSessionBinding when resultParams.effectiveCliSessionId is truthy, which traces back to output.sessionId from parseCliOutput (dist/execute.runtime-qivcEpPA.js:717-723). That fallback chain ends at params.fallbackSessionId — which is void 0 for the retry. So if claude-cli's parsed output doesn't surface its new sessionId (the text outputMode never does — dist/execute.runtime-qivcEpPA.js:275-289), effectiveCliSessionId is undefined, cliSessionBinding is undefined, and the persist branch is skipped.

In practice, the claude-cli backend in current use returns plain-text output (matching the text outputMode), so the new session id is never surfaced.

A defensive fix that doesn't depend on parser changes: after a successful retry, inspect the most-recent *.jsonl in ~/.claude/projects/<encoded-cwd>/ whose mtime falls inside the exec window, and use that filename's basename as the new sessionId. Alternatively, switch claude-cli backend defaults to output: "jsonl" so parseCliJsonl (called at dist/execute.runtime-qivcEpPA.js:281) can extract the session id from the JSONL records claude-cli emits.

Workaround we shipped

For now, a sidecar Node script polls the claude-cli sessions dir every 30s and rewrites cliSessionBindings.claude-cli.sessionId whenever the bound id is missing or 6h+ stale, atomically and scoped to agent:main:telegram:direct:* entries only. Source: https://github.com/MS-707/StarrOS-Personal-Life-Assistant-Dashboard/blob/main/scripts/benny-session-watchdog.mjs (link may be private — happy to paste inline if useful).

This eliminates the paired cli exec and restores cross-turn continuity. We're keeping it running until the upstream fix lands.

Adjacent observation

dist/extensions/telegram/bot-Dm2NPcQu.js:1429 sets RECENT_TELEGRAM_UPDATE_TTL_MS = 5 * 60_000 and the dedupe layer (lines 5618-5645) uses pendingUpdateKeys for in-flight collision detection. That part of the system looks correct — we can confirm the duplicate cli exec pattern is not a polling-dedup bug. Worth noting since the symptom is easy to misattribute.

extent analysis

TL;DR

The most likely fix involves updating the cliSessionBindings with the new session ID after a successful retry, either by parsing the JSONL output or by inspecting the most recent *.jsonl file in the ~/.claude/projects/<encoded-cwd>/ directory.

Guidance

  • Identify the source of the issue: the cliSessionBindings.claude-cli.sessionId is not being updated after a successful retry, causing the agent to lose conversational continuity.
  • Consider updating the buildCliRunResult function to set cliSessionBinding even when resultParams.effectiveCliSessionId is undefined, by inspecting the most recent *.jsonl file in the ~/.claude/projects/<encoded-cwd>/ directory.
  • Alternatively, switch the claude-cli backend defaults to output: "jsonl" to allow parseCliJsonl to extract the session ID from the JSONL records.
  • Verify the fix by checking the gateway.out logs for the paired cli exec lines and ensuring that the conversation continuity is maintained.

Example

// dist/attempt-execution.runtime-BFklk5cT.js:286-326
runCliWithSession(void 0).then(async (result) => {
  // ...
  const newSessionId = getNewSessionIdFromJsonl(); // implement this function to inspect the most recent *.jsonl file
  setCliSessionBinding(updatedEntry, /* ... */, { sessionId: newSessionId });
  // ...
});

// implement getNewSessionIdFromJsonl function
function getNewSessionIdFromJsonl() {
  const jsonlFiles = fs.readdirSync('~/.claude/projects/<encoded-cwd>/');
  const newestJsonlFile = jsonlFiles.sort((a, b) => fs.statSync(a).mtimeMs - fs.statSync(b).mtimeMs).pop();

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 Telegram DM: cliSessionBindings.claude-cli.sessionId not updated after session_expired retry — duplicate cli exec on every turn, mid-conversation amnesia [2 comments, 1 participants]