{"record":{"id":"73d68529acb28876","repo":"mastra-ai/mastra","slug":"no-oauth-authorization-is-pending-for-this-server","errorCode":null,"errorMessage":"No OAuth authorization is pending for this server. Call connect() first.","messagePattern":"No OAuth authorization is pending for this server\\. Call connect\\(\\) first\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/mcp/src/client/client.ts","lineNumber":791,"sourceCode":"  get authState(): MCPServerAuthState | undefined {\n    return this._authState;\n  }\n\n  /**\n   * Completes a pending OAuth authorization-code flow.\n   *\n   * Exchanges the authorization code captured at the redirect URI on the same\n   * transport that started the flow, then leaves the client ready to connect().\n   *\n   * @param authorizationCode - The authorization code captured at the redirect URI\n   * @throws {Error} If no authorization flow is pending for this server\n   *\n   * @internal\n   */\n  async finishAuth(authorizationCode: string): Promise<void> {\n    const pending = this.pendingAuthTransport;\n    if (!pending) {\n      throw new Error('No OAuth authorization is pending for this server. Call connect() first.');\n    }\n    this.pendingAuthTransport = undefined;\n    try {\n      await pending.finishAuth(authorizationCode);\n    } finally {\n      // The pending transport only ran the token exchange; the next connect() builds a fresh one.\n      void pending.close().catch(() => {});\n    }\n  }\n\n  private isConnected: Promise<boolean> | null = null;\n  private reconnectPromise: Promise<void> | null = null;\n  private lifecycleGeneration = 0;\n\n  /**\n   * Connects to the MCP server using the configured transport.\n   *\n   * Automatically detects transport type based on configuration (stdio vs HTTP).","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/mcp/src/client/client.ts#L773-L809","documentation":"finishAuth(authorizationCode) completes a pending OAuth token exchange started when connect() hit a 401 and deferred to an authorization flow. If no such pending authorization exists (pendingAuthTransport is undefined), this error is thrown because there is no transport waiting for the authorization code.","triggerScenarios":"Calling client.finishAuth(code) without a preceding connect() that triggered the OAuth flow; calling finishAuth twice; calling it after connect() already completed the exchange; calling it after a disconnect cleared the pending transport.","commonSituations":"OAuth callback invoked more than once (double browser redirect); application flow calls finishAuth unconditionally instead of only after a 401-driven redirect; server credentials already valid so connect() never triggered OAuth.","solutions":["Only call finishAuth after connect() rejected with an authorization requirement","Track whether an OAuth flow is in progress; gate the callback on that flag","Make the OAuth callback idempotent (ignore repeat calls)","If state was lost, call connect() again to restart the authorization flow"],"exampleFix":"// before\nawait client.finishAuth(req.query.code); // throws when nothing pending\n// after\ntry {\n  await client.finishAuth(req.query.code);\n} catch (e) {\n  if (!(e instanceof Error) || !e.message.includes('No OAuth authorization is pending')) throw e;\n  await client.connect(); // restart or ignore duplicate callback\n}","handlingStrategy":"try-catch","validationCode":"// only call finishAuth when connect() surfaced an authorization requirement\nlet authPending = false;\ntry { await client.connect(); } catch { authPending = true; }\nif (authPending) await client.finishAuth(code);","typeGuard":null,"tryCatchPattern":"try {\n  await client.finishAuth(code);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('No OAuth authorization is pending')) {\n    // duplicate callback or no flow in progress: ignore or restart connect()\n    return;\n  }\n  throw e;\n}","preventionTips":["Gate the OAuth callback handler on a 'flow in progress' flag","Make the callback idempotent against repeated redirects","Only call finishAuth after a 401-driven connect() failure","Restart with connect() to re-establish pending state if lost"],"tags":["mcp","oauth","auth","state"],"backgroundTag":"oauth-flow-state-missing","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}