{"record":{"id":"1a6b3ee32e636450","repo":"decolua/9router","slug":"no-authorization-url-returned-from-oauth-provider","errorCode":null,"errorMessage":"No authorization URL returned from OAuth provider","messagePattern":"No authorization URL returned from OAuth provider","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/shared/components/OAuthModal.js","lineNumber":367,"sourceCode":"            throw new Error(\"Port 56121 in use; close the conflicting process and retry\");\n          }\n        } catch (e) {\n          if (e?.message) throw e;\n          xaiProxyActive = false;\n        }\n      }\n\n      setAuthData({ ...data, redirectUri, codexServerSide, xaiServerSide });\n\n      // Guard: device_code providers return authUrl:null from /authorize. Never window.open(null)\n      // (browsers coerce it to the relative path \".../null\").\n      if (!data.authUrl) {\n        if (data.flowType === \"device_code\") {\n          throw new Error(\n            `Provider ${provider} uses device-code login but is not wired in the OAuth modal device-code list`\n          );\n        }\n        throw new Error(\"No authorization URL returned from OAuth provider\");\n      }\n\n      if (provider === \"codex\" && codexProxyActive) {\n        // Proxy active: callback will be handled server-side (auto-exchange) or via channels (fallback)\n        setStep(\"waiting\");\n        popupRef.current = window.open(data.authUrl, \"oauth_popup\", \"width=600,height=700\");\n        if (!popupRef.current) {\n          setStep(\"input\");\n        }\n      } else if (provider === \"xai\" && xaiProxyActive) {\n        setStep(\"waiting\");\n        popupRef.current = window.open(data.authUrl, \"oauth_popup\", \"width=600,height=700\");\n        if (!popupRef.current) {\n          setStep(\"input\");\n        }\n      } else if (!isLocalhost || provider === \"codex\" || provider === \"xai\") {\n        // Non-localhost or proxy failed: manual input mode\n        setStep(\"input\");","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/shared/components/OAuthModal.js#L349-L385","documentation":"Generic guard when /authorize returns no authUrl and the provider is not a known device-code provider. The modal refuses to call window.open(null) (browsers coerce null to the relative URL \".../null\"), so it throws instead. It indicates the server-side authorize step produced no redirect target for an authorization-code provider.","triggerScenarios":"GET /api/oauth/{provider}/authorize returned 200 but with authUrl:null/absent and flowType !== \"device_code\" — e.g. a misconfigured provider entry, an authorize handler bug, or an unknown provider id falling through to a stub handler.","commonSituations":"Provider registry entry missing an OAuth authorize implementation; partially configured provider (no client_id so no URL can be built); version mismatch between registry and dashboard after an upgrade.","solutions":["Check the /authorize response body (authUrl, flowType) to see what the server produced","Verify the provider is fully configured (client_id/secret) in gateway settings","Confirm the provider's registry entry implements the authorization-code flow","Report/fix the provider's authorize handler if authUrl is legitimately null"],"exampleFix":"null","handlingStrategy":"validation","validationCode":"const data = await res.json();\nif (!data || typeof data.authUrl !== 'string' || !data.authUrl.startsWith('http')) {\n  throw new Error('Provider did not return a valid authUrl');\n}","typeGuard":"const hasAuthUrl = (d) => !!d && typeof d.authUrl === 'string' && d.authUrl.length > 0;","tryCatchPattern":"try {\n  await startOAuthFlow();\n} catch (e) {\n  if (e.message.includes(\"No authorization URL\")) {\n    // check provider config; suggest re-configuring the provider\n  }\n}","preventionTips":["Fully configure the provider (client_id/secret) before attempting login","Keep gateway and dashboard versions in sync","Verify /authorize returns authUrl for authorization-code providers via a quick curl"],"tags":["oauth","null-url","configuration"],"backgroundTag":"oauth-authorize-url-missing","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}