{"record":{"id":"43fd830545ceaee4","repo":"CherryHQ/cherry-studio","slug":"oauth-authentication-failed-oautherror-instance","errorCode":null,"errorMessage":"OAuth authentication failed: ${oauthError instanceof Error ? oauthError.message : String(oauthError)}","messagePattern":"OAuth authentication failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/McpRuntimeService.ts","lineNumber":768,"sourceCode":"\n          try {\n            // Wait for the authorization code\n            const authCode = await callbackServer.waitForAuthCode()\n            getServerLogger(server).debug(`Received auth code`)\n\n            // Complete the OAuth flow\n            await transport.finishAuth(authCode)\n\n            getServerLogger(server).debug(`OAuth flow completed`)\n\n            const newTransport = await initTransport(typeOverride)\n            // Try to connect again\n            await client.connect(newTransport)\n\n            getServerLogger(server).debug(`Successfully authenticated`)\n          } catch (oauthError) {\n            getServerLogger(server).error(`OAuth authentication failed`, oauthError as Error)\n            throw new Error(\n              `OAuth authentication failed: ${oauthError instanceof Error ? oauthError.message : String(oauthError)}`\n            )\n          } finally {\n            // Clear the timeout and close the callback server\n            clearTimeout(timeoutId)\n            void callbackServer.close()\n          }\n        }\n\n        try {\n          // Bound the MCP `initialize` request so a non-responsive server fails fast via the\n          // SDK's own abort path instead of hanging. Use a 180s floor (activation runs once,\n          // generous headroom is cheap) while still honoring larger `server.timeout` values\n          // that the user explicitly configured. transport.start() latency remains bounded\n          // by the underlying fetch / child_process, matching v1.8.4 behavior.\n          const connectOptions: RequestOptions = {\n            timeout: Math.max((server.timeout ?? 0) * 1000, MCP_CONNECT_TIMEOUT_FLOOR_MS)\n          }","sourceCodeStart":750,"sourceCodeEnd":786,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/McpRuntimeService.ts#L750-L786","documentation":"Thrown when the OAuth authentication flow fails during `transport.finishAuth(authCode)` or the subsequent reconnection attempt (`client.connect(newTransport)`). The original error is logged via getServerLogger before being wrapped. This is a user-facing auth failure, not a transport-level protocol mismatch (those are handled by isTransportFallbackError separately).","triggerScenarios":"The OAuth callback delivers an auth code, but `finishAuth` rejects it (expired, already used, or PKCE mismatch), or the reconnection with the new transport fails due to network errors, invalid tokens, or server-side OAuth endpoint issues.","commonSituations":"Auth code expired before the user completed the consent flow; PKCE code_verifier doesn't match (storage issue); the OAuth server's token endpoint is down; redirect URI mismatch; the OAuth provider changed its API; network firewall blocks the token exchange.","solutions":["Retry the OAuth flow from scratch — the callback server and timeout are cleaned up automatically","Check the detailed server logs for the original oauthError message (logged before this throw)","Verify the OAuth provider's metadata endpoint is accessible and the redirect URI is correctly registered","If PKCE mismatch, clear the OAuth storage for this server and retry (storage.clear())","Ensure the system clock is accurate — token validation is time-sensitive"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"const MAX_OAUTH_RETRIES = 2\nfor (let attempt = 0; attempt <= MAX_OAUTH_RETRIES; attempt++) {\n  try {\n    await runtime.getOrCreateClient(server)\n    break\n  } catch (e) {\n    if (e instanceof Error && e.message.startsWith('OAuth authentication failed') && attempt < MAX_OAUTH_RETRIES) {\n      // Clear stale OAuth state and retry the full flow\n      await oauthStorage.clear()\n      continue\n    }\n    throw e\n  }\n}","preventionTips":["Ensure system clock is synchronized — OAuth tokens are time-sensitive","Clear OAuth storage before retrying if the flow was interrupted","Verify the OAuth provider's redirect URI matches what's registered on the server side","Don't let auth codes sit too long before completing the callback"],"tags":["mcp","oauth","authentication","network"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}