{"record":{"id":"50b19840ed8fddda","repo":"different-ai/openwork","slug":"the-oauth-provider-requested-authorization-without","errorCode":null,"errorMessage":"The OAuth provider requested authorization without an authorization URL.","messagePattern":"The OAuth provider requested authorization without an authorization URL\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/enterprise-mcp-client/src/enterprise-mcp-client.ts","lineNumber":492,"sourceCode":"            // servers are allowed to expose resources and/or prompts without\n            // implementing tools/list at all.\n            if (session.client.getServerCapabilities()?.tools) {\n              await session.client.listTools(undefined, session.requestOptions)\n            }\n            // OAuth connections must not be treated as member-connected merely\n            // because a provider exposes protocol negotiation and tools/list publicly.\n            // When no member credential exists, proactively run OAuth discovery\n            // so providers such as BigQuery can return an authorization URL\n            // without first issuing an MCP-level 401 challenge.\n            if (session.oauthProvider && !hadOAuthCredential) {\n              const authResult = await auth(session.oauthProvider, {\n                serverUrl: session.serverUrl,\n                fetchFn: session.observer.fetch,\n              })\n              const authorizeUrl = session.oauthProvider.authorizeUrl\n              if (authResult === \"REDIRECT\") {\n                if (!authorizeUrl) {\n                  throw new Error(\"The OAuth provider requested authorization without an authorization URL.\")\n                }\n                try {\n                  await closeWithinDeadline(() => session.client.close(), closeTimeoutMs)\n                } catch {\n                  // The bounded cleanup attempt must not discard a valid authorization URL.\n                }\n                return { status: \"needs_auth\", authorizeUrl }\n              }\n            }\n            try {\n              await closeWithinDeadline(() => session.client.close(), closeTimeoutMs)\n            } catch (error) {\n              throw new EnterpriseMcpClientError({\n                operationPhase: \"shutdown\",\n                requestPhase: session.observer.lastRequestPhase(),\n                cause: error,\n              })\n            }","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/enterprise-mcp-client/src/enterprise-mcp-client.ts#L474-L510","documentation":"During connect(), the MCP SDK's auth() flow returned \"REDIRECT\" (the provider wants the user sent to an authorization page), but the provider's authorizeUrl was never set (null). The client cannot redirect the user without a URL, so it throws a plain Error which is then wrapped by runOperation. This indicates the OAuth provider's redirect flow completed inconsistently — authorization was requested but no authorization endpoint URL was produced.","triggerScenarios":"auth(session.oauthProvider, ...) resolves with \"REDIRECT\" while session.oauthProvider.authorizeUrl is null — i.e. redirectToAuthorization was never invoked before the REDIRECT result, typically due to an SDK version mismatch or a provider metadata bug where the authorization endpoint is missing.","commonSituations":"Authorization server metadata lacking an authorization_endpoint; a broken/misconfigured provider behind a gateway; upgrading @modelcontextprotocol packages so auth() semantics diverge from the provider's expectations.","solutions":["Inspect the authorization server metadata (/.well-known/oauth-authorization-server) for the provider and confirm authorization_endpoint is present.","Update @modelcontextprotocol/client and @modelcontextprotocol/core to matching versions so auth() sets authorizeUrl before returning REDIRECT.","Retry the connect; if intermittent, capture the provider response to confirm whether metadata discovery partially failed.","Report/patch the provider: a REDIRECT result without an authorize URL is a contract violation on the provider side."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const res = await fetch(authorizationServerUrl.replace(/\\/$/, \"\") + \"/.well-known/oauth-authorization-server\");\nconst meta = await res.json();\nif (!meta.authorization_endpoint) throw new Error(\"Provider metadata lacks authorization_endpoint — REDIRECT will fail.\");","typeGuard":"function hasAuthorizeUrl(p: { authorizeUrl: string | null }): p is { authorizeUrl: string } {\n  return typeof p.authorizeUrl === \"string\" && p.authorizeUrl.length > 0;\n}","tryCatchPattern":"try {\n  await client.connect(input);\n} catch (e) {\n  if (String(e.cause?.message).includes(\"without an authorization URL\")) {\n    // provider metadata defect — refresh metadata and retry once, then surface to admin\n  }\n  throw e;\n}","preventionTips":["Pin matching versions of @modelcontextprotocol/client and @modelcontextprotocol/core.","Pre-flight the provider's authorization server metadata in health checks.","Alert on providers whose metadata omits authorization_endpoint.","Retry connect once on this error before escalating."],"tags":["oauth","provider-contract","redirect-flow"],"backgroundTag":"oauth-redirect-without-authorize-url","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}