openclaw - ✅(Solved) Fix cli-backend: "No conversation found" not detected as session_expired — stale session retry loop [1 pull requests, 1 comments, 2 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#61390Fetched 2026-04-08 02:59:07
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Timeline (top)
commented ×1cross-referenced ×1

isCliSessionExpiredErrorMessage() in pi-embedded-helpers does not match Claude CLI's "No conversation found with session ID: ..." error. The existing recovery logic (clear stale session + retry fresh) never triggers, causing an infinite retry loop with the same dead session ID across all fallback models.

Error Message

isCliSessionExpiredErrorMessage() in pi-embedded-helpers does not match Claude CLI's "No conversation found with session ID: ..." error. The existing recovery logic (clear stale session + retry fresh) never triggers, causing an infinite retry loop with the same dead session ID across all fallback models. Gateway error log shows 30+ consecutive failures over hours, all hitting the same dead session ID:

Root Cause

The pattern list in isCliSessionExpiredErrorMessage() includes "conversation not found" but not "no conversation found":

// pi-embedded-helpers — isCliSessionExpiredErrorMessage()
lower.includes("conversation not found")  // ← in the list
// but Claude CLI returns:
// "No conversation found with session ID: dfee08ff-..."
// lower: "no conversation found with session id: dfee08ff-..."
//
// "conversation not found" !== "no conversation found"
// ↑ missing "no " prefix — off by one word

Because the pattern doesn't match, classifyFailoverSignal() returns "unknown" instead of "session_expired", and the recovery path at agent-command line ~269 is never entered:

// agent-command — the recovery that SHOULD fire but doesn't:
if (err instanceof FailoverError && err.reason === "session_expired" && ...) {
    clearCliSession(updatedEntry, params.providerOverride);
    return runCliWithSession(void 0); // ← retry with fresh session
}

Fix Action

Fix

Add "no conversation found" to the pattern list:

 function isCliSessionExpiredErrorMessage(raw) {
     if (!raw) return false;
     const lower = raw.toLowerCase();
     return lower.includes("session not found") ||
            lower.includes("session does not exist") ||
            lower.includes("session expired") ||
            lower.includes("session invalid") ||
            lower.includes("conversation not found") ||
+           lower.includes("no conversation found") ||
            lower.includes("conversation does not exist") ||
            ...
 }

This enables the existing recovery in agent-command to fire: clear the stale cliSessionIds/claudeCliSessionId from the session store entry and retry with a fresh session.

Workaround

Manually patch pi-embedded-helpers-*.js in the installed openclaw dist to add the missing pattern, then restart the gateway.

PR fix notes

PR #61420: fix(cli-backend): detect missing Claude conversations as expired sessions

Description (problem / solution / changelog)

Summary

  • classify Claude CLI "No conversation found with session ID: ..." errors as session_expired
  • let the existing stale-session recovery path clear the dead CLI session id and retry fresh
  • add regression coverage for the missing "no conversation found" variant

Why

Claude CLI currently returns "No conversation found with session ID: ..." for stale sessions, but isCliSessionExpiredErrorMessage() only matched "conversation not found". That leaves the failure classified as unknown, so OpenClaw keeps retrying the same dead session id instead of clearing it.

Closes #61390

Verification

  • pnpm exec vitest run --config vitest.config.ts src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts --testNamePattern "Claude CLI"
  • pnpm exec oxfmt --check src/agents/pi-embedded-helpers/errors.ts src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts

Changed files

  • src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts (modified, +7/-0)
  • src/agents/pi-embedded-helpers/errors.ts (modified, +1/-0)

Code Example

// pi-embedded-helpers — isCliSessionExpiredErrorMessage()
lower.includes("conversation not found")  // ← in the list
// but Claude CLI returns:
// "No conversation found with session ID: dfee08ff-..."
// lower: "no conversation found with session id: dfee08ff-..."
//
// "conversation not found" !== "no conversation found"
// ↑ missing "no " prefix — off by one word

---

// agent-command — the recovery that SHOULD fire but doesn't:
if (err instanceof FailoverError && err.reason === "session_expired" && ...) {
    clearCliSession(updatedEntry, params.providerOverride);
    return runCliWithSession(void 0); // ← retry with fresh session
}

---

[model-fallback/decision] candidate_failed reason=unknown  ← should be session_expired
Embedded agent failed: All models failed (4):
  claude-cli/claude-opus-4-6: "No conversation found with session ID: 40c32f08-..."
  claude-cli/claude-sonnet-4-6: "No conversation found with session ID: 40c32f08-..."
  claude-cli/sonnet-4.6: "No conversation found with session ID: 40c32f08-..."
  claude-cli/sonnet-4.5: "No conversation found with session ID: 40c32f08-..."

---

function isCliSessionExpiredErrorMessage(raw) {
     if (!raw) return false;
     const lower = raw.toLowerCase();
     return lower.includes("session not found") ||
            lower.includes("session does not exist") ||
            lower.includes("session expired") ||
            lower.includes("session invalid") ||
            lower.includes("conversation not found") ||
+           lower.includes("no conversation found") ||
            lower.includes("conversation does not exist") ||
            ...
 }
RAW_BUFFERClick to expand / collapse

Summary

isCliSessionExpiredErrorMessage() in pi-embedded-helpers does not match Claude CLI's "No conversation found with session ID: ..." error. The existing recovery logic (clear stale session + retry fresh) never triggers, causing an infinite retry loop with the same dead session ID across all fallback models.

Root Cause

The pattern list in isCliSessionExpiredErrorMessage() includes "conversation not found" but not "no conversation found":

// pi-embedded-helpers — isCliSessionExpiredErrorMessage()
lower.includes("conversation not found")  // ← in the list
// but Claude CLI returns:
// "No conversation found with session ID: dfee08ff-..."
// lower: "no conversation found with session id: dfee08ff-..."
//
// "conversation not found" !== "no conversation found"
// ↑ missing "no " prefix — off by one word

Because the pattern doesn't match, classifyFailoverSignal() returns "unknown" instead of "session_expired", and the recovery path at agent-command line ~269 is never entered:

// agent-command — the recovery that SHOULD fire but doesn't:
if (err instanceof FailoverError && err.reason === "session_expired" && ...) {
    clearCliSession(updatedEntry, params.providerOverride);
    return runCliWithSession(void 0); // ← retry with fresh session
}

Observed Behavior

  1. A Claude CLI session expires or becomes unreachable (e.g., after context exhaustion, rate limit kills the session, or gateway restart)
  2. Gateway tries --resume <stale-id> → Claude CLI returns "No conversation found with session ID: <id>"
  3. Pattern doesn't match → reason = "unknown" → no session cleanup
  4. Model fallback kicks in, but all fallback models reuse the same stale session ID
  5. All 4 models fail → "All models failed (4)" → user sees "Something went wrong"
  6. Next message → same stale ID → same failure → permanent loop until manual intervention

Gateway error log shows 30+ consecutive failures over hours, all hitting the same dead session ID:

[model-fallback/decision] candidate_failed reason=unknown  ← should be session_expired
Embedded agent failed: All models failed (4):
  claude-cli/claude-opus-4-6: "No conversation found with session ID: 40c32f08-..."
  claude-cli/claude-sonnet-4-6: "No conversation found with session ID: 40c32f08-..."
  claude-cli/sonnet-4.6: "No conversation found with session ID: 40c32f08-..."
  claude-cli/sonnet-4.5: "No conversation found with session ID: 40c32f08-..."

Fix

Add "no conversation found" to the pattern list:

 function isCliSessionExpiredErrorMessage(raw) {
     if (!raw) return false;
     const lower = raw.toLowerCase();
     return lower.includes("session not found") ||
            lower.includes("session does not exist") ||
            lower.includes("session expired") ||
            lower.includes("session invalid") ||
            lower.includes("conversation not found") ||
+           lower.includes("no conversation found") ||
            lower.includes("conversation does not exist") ||
            ...
 }

This enables the existing recovery in agent-command to fire: clear the stale cliSessionIds/claudeCliSessionId from the session store entry and retry with a fresh session.

Environment

  • OpenClaw: 2026.4.2 (d74a122)
  • Claude Code CLI: 2.1.92
  • Provider: claude-cli (OAuth, firstParty)
  • OS: macOS Darwin 25.3.0

Workaround

Manually patch pi-embedded-helpers-*.js in the installed openclaw dist to add the missing pattern, then restart the gateway.

extent analysis

TL;DR

Update the isCliSessionExpiredErrorMessage() function in pi-embedded-helpers to include the missing pattern "no conversation found" to fix the infinite retry loop issue.

Guidance

  • Add "no conversation found" to the pattern list in isCliSessionExpiredErrorMessage() to enable the existing recovery logic.
  • Verify the fix by checking the gateway error log for the presence of "session_expired" reason after a session expiration or failure.
  • To mitigate the issue temporarily, manually patch pi-embedded-helpers-*.js in the installed openclaw dist to add the missing pattern, then restart the gateway.
  • Ensure the recovery path in agent-command is triggered correctly by checking for the presence of "session_expired" reason and the subsequent clearing of the stale session ID.

Example

function isCliSessionExpiredErrorMessage(raw) {
    if (!raw) return false;
    const lower = raw.toLowerCase();
    return lower.includes("session not found") ||
           lower.includes("session does not exist") ||
           lower.includes("session expired") ||
           lower.includes("session invalid") ||
           lower.includes("conversation not found") ||
           lower.includes("no conversation found") ||
           lower.includes("conversation does not exist") ||
           // ...
}

Notes

The fix assumes that the issue is solely due to the missing pattern in isCliSessionExpiredErrorMessage(). If the issue persists after applying the fix, further investigation may be required.

Recommendation

Apply the workaround by manually patching pi-embedded-helpers-*.js in the installed openclaw dist to add the missing pattern, then restart the gateway, as this provides a temporary solution until the official fix is available.

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 - ✅(Solved) Fix cli-backend: "No conversation found" not detected as session_expired — stale session retry loop [1 pull requests, 1 comments, 2 participants]