decolua/9router · error · Error

No authorization URL returned from OAuth provider

Error message

No authorization URL returned from OAuth provider

What it means

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.

Source

Thrown at src/shared/components/OAuthModal.js:367

            throw new Error("Port 56121 in use; close the conflicting process and retry");
          }
        } catch (e) {
          if (e?.message) throw e;
          xaiProxyActive = false;
        }
      }

      setAuthData({ ...data, redirectUri, codexServerSide, xaiServerSide });

      // Guard: device_code providers return authUrl:null from /authorize. Never window.open(null)
      // (browsers coerce it to the relative path ".../null").
      if (!data.authUrl) {
        if (data.flowType === "device_code") {
          throw new Error(
            `Provider ${provider} uses device-code login but is not wired in the OAuth modal device-code list`
          );
        }
        throw new Error("No authorization URL returned from OAuth provider");
      }

      if (provider === "codex" && codexProxyActive) {
        // Proxy active: callback will be handled server-side (auto-exchange) or via channels (fallback)
        setStep("waiting");
        popupRef.current = window.open(data.authUrl, "oauth_popup", "width=600,height=700");
        if (!popupRef.current) {
          setStep("input");
        }
      } else if (provider === "xai" && xaiProxyActive) {
        setStep("waiting");
        popupRef.current = window.open(data.authUrl, "oauth_popup", "width=600,height=700");
        if (!popupRef.current) {
          setStep("input");
        }
      } else if (!isLocalhost || provider === "codex" || provider === "xai") {
        // Non-localhost or proxy failed: manual input mode
        setStep("input");

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Check the /authorize response body (authUrl, flowType) to see what the server produced
  2. Verify the provider is fully configured (client_id/secret) in gateway settings
  3. Confirm the provider's registry entry implements the authorization-code flow
  4. Report/fix the provider's authorize handler if authUrl is legitimately null

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

const data = await res.json();
if (!data || typeof data.authUrl !== 'string' || !data.authUrl.startsWith('http')) {
  throw new Error('Provider did not return a valid authUrl');
}

Type guard

const hasAuthUrl = (d) => !!d && typeof d.authUrl === 'string' && d.authUrl.length > 0;

Try / catch

try {
  await startOAuthFlow();
} catch (e) {
  if (e.message.includes("No authorization URL")) {
    // check provider config; suggest re-configuring the provider
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/1a6b3ee32e636450. Report an issue: GitHub.