{"record":{"id":"cb458db4fe5c5e8c","repo":"Hmbown/CodeWhale","slug":"unexpected-mcp-oauth-state-while-preparing-stored","errorCode":null,"errorMessage":"unexpected MCP OAuth state while preparing stored credentials","messagePattern":"unexpected MCP OAuth state while preparing stored credentials","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/oauth.rs","lineNumber":187,"sourceCode":"    async fn from_stored_tokens(\n        server_name: &str,\n        url: &str,\n        mut tokens: StoredMcpOAuthTokens,\n        default_headers: HeaderMap,\n    ) -> Result<Self> {\n        refresh_expires_in_from_timestamp(&mut tokens);\n        let client = apply_default_headers(crate::tls::reqwest_client_builder(), &default_headers)\n            .build()\n            .context(\"building MCP OAuth metadata client\")?;\n        let mut state = OAuthState::new(url.to_string(), Some(client)).await?;\n        state\n            .set_credentials(&tokens.client_id, tokens.token_response.0.clone())\n            .await\n            .context(\"installing stored MCP OAuth credentials\")?;\n\n        let manager = match state {\n            OAuthState::Authorized(manager) | OAuthState::Unauthorized(manager) => manager,\n            _ => bail!(\"unexpected MCP OAuth state while preparing stored credentials\"),\n        };\n\n        Ok(Self {\n            inner: Arc::new(McpOAuthRuntimeInner {\n                server_name: server_name.to_string(),\n                url: url.to_string(),\n                manager: Arc::new(Mutex::new(manager)),\n                last_tokens: Mutex::new(Some(tokens)),\n            }),\n        })\n    }\n\n    pub async fn authorization_header(&self) -> Result<Option<String>> {\n        self.refresh_if_needed().await?;\n        let credentials = {\n            let guard = self.inner.manager.lock().await;\n            let (_client_id, credentials) = guard\n                .get_credentials()","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/mcp/oauth.rs#L169-L205","documentation":"When restoring persisted MCP OAuth credentials, a fresh OAuthState is created and set_credentials installs the stored client ID and token response; afterwards the state must have settled into Authorized or Unauthorized to yield a usable manager. Any other enum variant (e.g. a mid-flow state such as Session) means the stored credentials do not map onto a settled state, and construction aborts with this bail.","triggerScenarios":"Stored credentials were persisted mid-flow, are from an older format/version whose state transitions differ, or are corrupted such that set_credentials leaves the state machine in a non-settled variant.","commonSituations":"Upgrading the app with a stale token cache; an interrupted OAuth login leaving partial state; hand-edited or migrated token storage.","solutions":["Clear the stored MCP OAuth credentials for that server (remove the token cache entry)","Re-run the OAuth login flow to write fresh, fully-settled credentials","Verify the stored token JSON matches the current credentials schema if it was migrated between versions"],"exampleFix":"# before: stale stored credentials keep OAuthState mid-flow\nrm ~/.local/state/codewhale/mcp-oauth/<server>.json  # adjust to your token cache path\ncodewhale mcp oauth login <server>","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match McpOAuthRuntime::with_stored_credentials(server_name, url, default_headers, tokens).await {\n    Ok(rt) => rt,\n    Err(e) if e.to_string().contains(\"unexpected MCP OAuth state\") => {\n        // stored credentials are unusable: discard and fall back to a fresh login\n        clear_stored_mcp_oauth_tokens(server_name);\n        run_oauth_login(server_name, server).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Clear stored MCP OAuth credentials after version upgrades if login fails to initialize","Persist tokens only after the OAuth flow fully settles, never mid-flow","Treat token-cache schema changes as a migration step with a clean re-login fallback"],"tags":["mcp","oauth","state","credentials","migration","auth"],"backgroundTag":"invalid-oauth-state","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}