decolua/9router · warning

xai discovery ${field} is empty

Error message

xai discovery ${field} is empty

What it means

validateXaiOAuthEndpoint (src/lib/oauth/providerHelpers.js) validates endpoints returned by the xAI OIDC discovery document before the OAuth flow uses them. It rejects empty values, non-URLs, non-https URLs, and hosts outside x.ai. This specific error fires when the discovery document's field (authorization_endpoint or token_endpoint) is missing, empty, or whitespace after trimming. In practice discoverXaiEndpoints wraps the call in try/catch and falls back to static xAI endpoints, so this surfaces only if the catch is bypassed or the helper is called directly.

Source

Thrown at src/lib/oauth/providerHelpers.js:5

const BASE64_BLOCK_SIZE = 4;

function validateXaiOAuthEndpoint(rawUrl, field) {
  const value = String(rawUrl || "").trim();
  if (!value) throw new Error(`xai discovery ${field} is empty`);
  let parsed;
  try { parsed = new URL(value); } catch (err) {
    throw new Error(`xai discovery ${field} is invalid: ${err.message}`);
  }
  if (parsed.protocol !== "https:") throw new Error(`xai discovery ${field} must use https: ${value}`);
  const host = parsed.hostname.toLowerCase().trim();
  if (host !== "x.ai" && !host.endsWith(".x.ai")) {
    throw new Error(`xai discovery ${field} host ${host} is not on x.ai`);
  }
  return value;
}

function decodeXaiIdTokenEmail(idToken) {
  if (!idToken || typeof idToken !== "string") return undefined;
  const parts = idToken.split(".");
  if (parts.length !== 3) return undefined;
  try {
    const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Rely on the built-in fallback: discoverXaiEndpoints catches this and uses XAI_CONFIG.authorizeUrl/tokenUrl; verify you are not swallowing the fallback path.
  2. Re-run discovery later — a transiently bad discovery response is usually upstream.
  3. If calling validateXaiOAuthEndpoint directly, pass the discovered field value or the static config value.
  4. Check network/proxy integrity: a 200 with {} suggests a captive portal or broken proxy.

Example fix

// before: data.authorization_endpoint missing -> throws
const { authorizeUrl } = { authorizeUrl: validateXaiOAuthEndpoint(data.authorization_endpoint, 'authorization_endpoint') };
// after: guard before validating
const authEp = data.authorization_endpoint || XAI_CONFIG.authorizeUrl;
const authorizeUrl = validateXaiOAuthEndpoint(authEp, 'authorization_endpoint');
Defensive patterns

Strategy: fallback

Validate before calling

const raw = data.authorization_endpoint;
if (typeof raw !== 'string' || !raw.trim()) {
  endpoints = { authorizeUrl: XAI_CONFIG.authorizeUrl, tokenUrl: XAI_CONFIG.tokenUrl }; // static fallback
}

Type guard

function isNonEmptyUrlString(v) {
  return typeof v === 'string' && v.trim().length > 0;
}

Try / catch

try {
  const authUrl = validateXaiOAuthEndpoint(data.authorization_endpoint, 'authorization_endpoint');
  // use authUrl
} catch (err) {
  if (/xai discovery .* is empty/.test(err.message)) {
    return { authorizeUrl: XAI_CONFIG.authorizeUrl, tokenUrl: XAI_CONFIG.tokenUrl };
  }
  throw err;
}

Prevention

When it happens

Trigger: discoverXaiEndpoints fetches the xAI discovery URL successfully (res.ok) but the JSON has no authorization_endpoint/token_endpoint, or the field is an empty string; also any direct call to validateXaiOAuthEndpoint('', 'token_endpoint').

Common situations: xAI discovery endpoint temporarily serving an error-shaped 2xx JSON body, a proxy/MITM returning empty JSON {}, cached/intercepted responses, or tests calling the validator with undefined/empty input.

Related errors


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