openclaw - ✅(Solved) Fix [Bug]: CLI sessions: Gemini CLI not covered by Claude CLI session persistence fixes (#69679 / #70106 / #70132) — gateway restart still mints a fresh conversation [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#70973Fetched 2026-04-24 10:37:16
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
labeled ×2

Three fixes in v2026.4.22 restored CLI session continuity across gateway restarts — but all three are scoped to claude-cli only:

  • #69679 — CLI/Claude: keep compatible claude-cli runs on a warm stdio session and resume from the stored Claude session after Gateway restarts or idle exits
  • #70106 — CLI sessions: keep provider-owned CLI sessions through implicit daily expiry ... retain Claude CLI binding metadata across gateway agent requests
  • #70132 — CLI/Claude: keep Claude CLI session bindings stable across OAuth access-token refreshes, so gateway restarts continue the same Claude conversation instead of minting a fresh one

The same regression still affects google-gemini-cli: after any launchctl unload && load (or openclaw gateway restart), the next request to a gemini-cli-backed agent starts a brand-new conversation with no memory of prior turns. Before ~v2026.4.21 this worked transparently.

Root Cause

Three fixes in v2026.4.22 restored CLI session continuity across gateway restarts — but all three are scoped to claude-cli only:

  • #69679 — CLI/Claude: keep compatible claude-cli runs on a warm stdio session and resume from the stored Claude session after Gateway restarts or idle exits
  • #70106 — CLI sessions: keep provider-owned CLI sessions through implicit daily expiry ... retain Claude CLI binding metadata across gateway agent requests
  • #70132 — CLI/Claude: keep Claude CLI session bindings stable across OAuth access-token refreshes, so gateway restarts continue the same Claude conversation instead of minting a fresh one

The same regression still affects google-gemini-cli: after any launchctl unload && load (or openclaw gateway restart), the next request to a gemini-cli-backed agent starts a brand-new conversation with no memory of prior turns. Before ~v2026.4.21 this worked transparently.

Fix Action

Fixed

PR fix notes

PR #71076: fix(cli): fingerprint gemini cli credentials in auth epoch

Description (problem / solution / changelog)

Summary

  • Problem: getLocalCliCredentialFingerprint in src/agents/cli-auth-epoch.ts:102 dispatches on provider through a switch with explicit branches for claude-cli and codex-cli; every other CLI provider — including google-gemini-cli — falls through to the default branch at src/agents/cli-auth-epoch.ts:117 and returns undefined. As a result, the authEpoch value stored alongside a google-gemini-cli CLI session binding never binds to the user's local Gemini OAuth state at ~/.gemini/oauth_creds.json. The reporter of #70973 observed that Gemini CLI sessions do not benefit from the session-persistence guarantees Claude CLI and Codex CLI rely on, and this missing switch branch is the concrete code-level gap.
  • Root Cause: The authEpoch contract is "hash a stable fingerprint of whatever local credential state exists, so the stored session binding can detect credential transitions without churning on normal OAuth access-token refresh." The per-provider getLocalCliCredentialFingerprint is the only place where a CLI's local credential file feeds into that contract. Because Gemini CLI is not registered in the switch, the local side of the computation is simply absent: every recomputed authEpoch for a Gemini CLI session is identical to the stored one on the local-only path (both undefined), which means resolveCliSessionReuse cannot observe any transition between "local credential present" and "local credential absent" at all. Asymmetric moves between "local-only" and "local + auth-profile" states that are caught today for Claude and Codex therefore fall through for Gemini. Neighbouring work was checked to make sure this is the real seam: hasProviderOwnedSession in src/auto-reply/reply/session.ts:142 already reads entry.providerOverride ?? entry.modelProvider through provider-agnostic getCliSessionBinding, so Gemini entries already skip implicit daily-expiry correctly (verified with a scoped probe against initSessionState); and the warm-stdio path in src/agents/cli-runner/claude-live-session.ts:75 is gated on backend.liveSession === "claude-stdio" + backend.output === "jsonl" + backend.input === "stdin", which the Gemini backend at extensions/google/cli-backend.ts:14 does not declare (output: "json", input: "arg", no liveSession) — warm-stdio is orthogonal to the Gemini transport and is not the root cause here.
  • Fix: Add a google-gemini-cli branch to getLocalCliCredentialFingerprint, reading ~/.gemini/oauth_creds.json through the same cached-reader pattern Codex and MiniMax already use (readCachedCliCredential + readPortalCliOauthCredentials). encodeGeminiCredential delegates to the shared encodeOAuthIdentity helper. Since GeminiCliCredential carries only access / refresh / expires and no identity fields, encodeOAuthIdentity yields a provider-keyed constant — the exact same identity-less OAuth behavior the Claude CLI OAuth branch already produces today. This keeps the auth-epoch contract symmetric across Claude / Codex / Gemini on the local path: the stored authEpoch is defined when local creds exist, flips cleanly on the "credential appears / disappears" boundary, and does not churn under routine OAuth token rotation. No identity material, token material, or JWT content is hashed. No behavior change for Claude / Codex: their branches are untouched and produce bit-for-bit identical epochs.
  • What changed:
    • src/agents/cli-credentials.ts: add GEMINI_CLI_CREDENTIALS_RELATIVE_PATH = ".gemini/oauth_creds.json", a GeminiCliCredential type (access / refresh / expires — same shape as MiniMaxCliCredential), a geminiCliCache slot, a resolveGeminiCliCredentialsPath helper, and readGeminiCliCredentials / readGeminiCliCredentialsCached that reuse the existing readPortalCliOauthCredentials parser and the shared readCachedCliCredential caching wrapper; extend resetCliCredentialCachesForTest to clear the new cache.
    • src/agents/cli-auth-epoch.ts: import readGeminiCliCredentialsCached and GeminiCliCredential; register the reader on CliAuthEpochDeps and its defaults for test injection; add encodeGeminiCredential that delegates to encodeOAuthIdentity; add the case "google-gemini-cli" branch mirroring the existing Codex and Claude branches.
    • src/agents/cli-auth-epoch.test.ts: extend the existing "no local or auth-profile credentials" case to stub the new dep; add one test asserting that the Gemini local-credential epoch is defined and stays stable across access/refresh rotation — matching the existing identity-less Claude OAuth assertion.
  • What did NOT change (scope boundary):
    • resolveCliAuthEpoch's public signature, the overall parts ordering, or the hash construction. Existing Claude / Codex callers compute bit-for-bit identical epochs.
    • resolveCliSessionReuse and the cliSessionBindings schema. No new fields, no migrations. Existing bindings on disk keep working; the authEpochVersion upgrade semantics already in the file continue to handle the transition.
    • The shared encodeOAuthIdentity helper and the Claude / Codex encoders, which keep returning the exact same strings for pre-existing inputs.
    • readPortalCliOauthCredentials and the MiniMax reader — reused, not modified.
    • extensions/google/ — no extension code changes. The new reader lives alongside the existing Claude and Codex readers to stay consistent with the current core pattern. (Longer term the core switch should dispatch through a plugin-owned seam per CLAUDE.md's extension-agnostic rule; that refactor applies to the Claude and Codex branches too and is intentionally out of scope for a bug fix.)
    • Warm-stdio / claude-live-session.ts — not applicable to the Gemini transport (output: "json", input: "arg").
    • No JWT decoding, no PII hashing, no provider-specific credential parsing.

Reproduction

  1. Set up google-gemini-cli via Gemini CLI's own gemini auth login so ~/.gemini/oauth_creds.json exists with real access_token / refresh_token / expiry_date.
  2. Start OpenClaw with a google-gemini-cli/* default model and have a multi-turn conversation. A cliSessionBindings["google-gemini-cli"] entry is persisted. Before this fix the stored authEpoch is undefined on the local-only path; after this fix it is defined.
  3. On subsequent turns (including across gateway restarts) the recomputed local authEpoch matches the stored one as long as the Gemini credential file still exists, and the session is reused. When the credential file disappears the epoch flips, resolveCliSessionReuse returns { invalidatedReason: "auth-epoch" }, and OpenClaw mints a fresh binding — matching the behavior Claude and Codex already have.

Risk / Mitigation

  • Risk: Reading a new file from disk (~/.gemini/oauth_creds.json) on an auth-epoch hot path could regress startup / latency.
  • Mitigation: The read goes through the existing readCachedCliCredential helper with a 5000 ms TTL and readFileMtimeMs-based source-fingerprinting, identical to the Codex reader at src/agents/cli-auth-epoch.ts:105. When the file is absent the read returns null in a single stat call. No new crypto, no network I/O, no new parsing logic — the readPortalCliOauthCredentials helper already ships for MiniMax.
  • Risk: The encoding produces the same constant for any Gemini OAuth credential, so a Google-account switch on the local credential file alone is not surfaced through this path.
  • Mitigation: This matches the current Claude CLI OAuth behavior exactly — ClaudeCliCredential has the same no-identity shape and encodeOAuthIdentity returns a constant for it as well. Callers that need account-level distinction already rely on the auth-profile arm of resolveCliAuthEpoch, which is unchanged. Intentionally symmetric with the existing accepted behavior; no new divergence introduced.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Agents
  • CLI sessions
  • Provider: Google Gemini CLI
  • Tests

Linked Issue/PR

Fixes #70973

Changed files

  • src/agents/cli-auth-epoch.test.ts (modified, +29/-0)
  • src/agents/cli-auth-epoch.ts (modified, +18/-0)
  • src/agents/cli-credentials.ts (modified, +38/-0)
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

Three fixes in v2026.4.22 restored CLI session continuity across gateway restarts — but all three are scoped to claude-cli only:

  • #69679 — CLI/Claude: keep compatible claude-cli runs on a warm stdio session and resume from the stored Claude session after Gateway restarts or idle exits
  • #70106 — CLI sessions: keep provider-owned CLI sessions through implicit daily expiry ... retain Claude CLI binding metadata across gateway agent requests
  • #70132 — CLI/Claude: keep Claude CLI session bindings stable across OAuth access-token refreshes, so gateway restarts continue the same Claude conversation instead of minting a fresh one

The same regression still affects google-gemini-cli: after any launchctl unload && load (or openclaw gateway restart), the next request to a gemini-cli-backed agent starts a brand-new conversation with no memory of prior turns. Before ~v2026.4.21 this worked transparently.

Steps to reproduce

  1. Have an ongoing multi-turn conversation with a gemini-cli-backed agent over Discord or Telegram.
  2. Restart the gateway (launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist && launchctl load ~/Library/LaunchAgents/ai.openclaw.gateway.plist).
  3. Send the next message from the same channel/DM.

Expected behavior

agent resumes the stored session, still remembers prior turns.

Actual behavior

agent responds as if it has never met the user before. Prior session file under ~/.openclaw/agents/<agentId>/sessions/ is not resumed; a new session id is minted.

OpenClaw version

2026.4.22

Operating system

macOS 26.3.1

Install method

No response

Model

google-gemini-cli/gemini-3-pro-preview", "google-gemini-cli/gemini-3.1-pro-preview"

Provider / routing chain

openclaw -> google-gemini-cli -> gemini-3.1-pro-preview

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Impact and severity

No response

Additional information

No response

extent analysis

TL;DR

The issue can be addressed by adapting the fixes from claude-cli to google-gemini-cli to restore session continuity across gateway restarts.

Guidance

  • Review the changes made in v2026.4.22 for claude-cli (issues #69679, #70106, #70132) to understand how session continuity was achieved.
  • Adapt these changes to apply to google-gemini-cli, focusing on maintaining compatible runs, retaining session metadata, and stabilizing session bindings across OAuth token refreshes.
  • Test the adapted changes with the provided steps to reproduce, verifying that the session is resumed correctly after a gateway restart.
  • Consider implementing a similar mechanism for implicit daily expiry of provider-owned CLI sessions to ensure long-term session continuity.

Example

No specific code example can be provided without more details on the implementation, but the focus should be on adapting the session management logic from claude-cli to google-gemini-cli.

Notes

The solution depends on the specific implementation details of google-gemini-cli and how it handles sessions, which are not fully provided in the issue description. Thus, the guidance is towards adapting existing fixes rather than providing a direct code solution.

Recommendation

Apply a workaround by adapting the claude-cli fixes to google-gemini-cli, as the issue seems to stem from the lack of similar session continuity measures in google-gemini-cli.

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…

FAQ

Expected behavior

agent resumes the stored session, still remembers prior turns.

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 [Bug]: CLI sessions: Gemini CLI not covered by Claude CLI session persistence fixes (#69679 / #70106 / #70132) — gateway restart still mints a fresh conversation [1 pull requests, 1 participants]