{"record":{"id":"3c2f16262e93229d","repo":"zeroclaw-labs/zeroclaw","slug":"gemini-cli-oauth-token-expired-and-no-refresh-toke","errorCode":null,"errorMessage":"Gemini CLI OAuth token expired and no refresh_token available — re-run `gemini` to authenticate","messagePattern":"Gemini CLI OAuth token expired and no refresh_token available — re-run `gemini` to authenticate","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/gemini.rs","lineNumber":834,"sourceCode":"            .is_none_or(|exp| exp <= now_millis.saturating_add(60_000));\n\n        if needs_refresh {\n            if let Some(ref refresh_token) = guard.refresh_token {\n                let refreshed = refresh_gemini_cli_token_async(\n                    refresh_token,\n                    guard.client_id.as_deref(),\n                    guard.client_secret.as_deref(),\n                )\n                .await?;\n                ::zeroclaw_log::record!(\n                    INFO,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),\n                    \"Gemini CLI OAuth token refreshed successfully (runtime)\"\n                );\n                guard.access_token = refreshed.access_token;\n                guard.expiry_millis = refreshed.expiry_millis;\n            } else {\n                anyhow::bail!(\n                    \"Gemini CLI OAuth token expired and no refresh_token available — re-run `gemini` to authenticate\"\n                );\n            }\n        }\n\n        Ok(guard.access_token.clone())\n    }\n\n    /// Rotate to the next available OAuth credentials file and swap state.\n    /// Returns `true` when rotation succeeded.\n    async fn rotate_oauth_credential(\n        &self,\n        state: &Arc<tokio::sync::Mutex<OAuthTokenState>>,\n    ) -> bool {\n        if self.oauth_cred_paths.len() <= 1 {\n            return false;\n        }\n","sourceCodeStart":816,"sourceCodeEnd":852,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/gemini.rs#L816-L852","documentation":"The Gemini CLI OAuth credential state holds an access token that is expired (or within the 60-second pre-expiry refresh buffer, or of unknown expiry) and has no refresh_token, so get_valid_oauth_token cannot renew it in-process. The only recovery is re-authenticating through the Gemini CLI, as the message states.","triggerScenarios":"get_valid_oauth_token needs a refresh but guard.refresh_token is None: the loaded ~/.gemini credential file contains an access token without a refresh_token field - an old format, a partially-written file, or a token saved before full consent.","commonSituations":"Upgraded or uninstalled Gemini CLI leaving stale credentials; credential files copied or edited without the refresh_token field; headless setups that never completed the full CLI login.","solutions":["Run `gemini` and log in again so a complete oauth_creds.json (with refresh_token) is written","Delete the stale credential files under the Gemini CLI config dir and log in fresh","Or configure a Gemini API key on the alias to bypass CLI OAuth entirely","If this recurs after every restart, check that nothing truncates or rewrites the credential file"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Check credential completeness before building the provider\nif gemini_cli_creds_present() && !gemini_cli_creds_have_refresh_token() {\n    // incomplete credentials: fail fast with a re-login hint at startup\n    anyhow::bail!(\"Gemini CLI creds lack refresh_token; re-run `gemini`\");\n}","typeGuard":null,"tryCatchPattern":"match provider.chat(/* ... */).await {\n    Ok(resp) => Ok(resp),\n    Err(e) if e.to_string().contains(\"no refresh_token available\") => {\n        // terminal for this credential set: instruct `gemini` re-login,\n        // or fall back to an API-key-based gemini alias\n        fallback_to_api_key_alias().await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["After Gemini CLI upgrades, re-login once so complete credentials are written","Never hand-edit or truncate oauth_creds.json","Use GeminiModelProvider::has_cli_credentials() plus a refresh_token check at startup","Configure an API-key alias as fallback for unattended deployments"],"tags":["gemini","oauth","token-expired","google","rust"],"backgroundTag":"oauth-token-expired-no-refresh-token","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}