decolua/9router · warning

xai discovery ${field} is invalid: ${err.message}

Error message

xai discovery ${field} is invalid: ${err.message}

What it means

validateXaiOAuthEndpoint parses the discovered endpoint with the URL constructor; when parsing fails it rethrows a wrapped error naming which field failed and the underlying URL parser message. It guards the OAuth flow from garbage endpoint strings in the xAI discovery document. discoverXaiEndpoints normally catches this and falls back to static x.ai endpoints.

Source

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

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, "/");
    const padding = (BASE64_BLOCK_SIZE - (base64.length % BASE64_BLOCK_SIZE)) % BASE64_BLOCK_SIZE;
    const json = Buffer.from(base64 + "=".repeat(padding), "base64").toString("utf8");
    const payload = JSON.parse(json);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Let discoverXaiEndpoints' try/catch fall back to the static XAI_CONFIG endpoints; confirm your caller doesn't rethrow.
  2. Inspect the raw discovery response (curl the discoveryUrl) to see what malformed value is being served.
  3. If you control discoveryUrl, point it back at the official xAI well-known endpoint.
  4. Pre-validate with new URL(value) in a try/catch before calling the OAuth flow.

Example fix

// before
const tokenUrl = validateXaiOAuthEndpoint('x.ai/api/oauth/token', 'token_endpoint'); // throws: Invalid URL
// after
const tokenUrl = validateXaiOAuthEndpoint('https://x.ai/api/oauth/token', 'token_endpoint');
Defensive patterns

Strategy: validation

Validate before calling

function isParseableUrl(v) {
  if (typeof v !== 'string') return false;
  try { new URL(v.trim()); return true; } catch { return false; }
}
// use: isParseableUrl(data.token_endpoint) ? validateXaiOAuthEndpoint(data.token_endpoint, 'token_endpoint') : fallback

Type guard

function isHttpUrl(v) {
  try { return new URL(String(v).trim()) instanceof URL; } catch { return false; }
}

Try / catch

try {
  const tokenUrl = validateXaiOAuthEndpoint(data.token_endpoint, 'token_endpoint');
  // use tokenUrl
} catch (err) {
  if (/xai discovery .* is invalid:/.test(err.message)) {
    console.warn('xAI discovery endpoint unparseable, using static fallback', err.message);
    return { authorizeUrl: XAI_CONFIG.authorizeUrl, tokenUrl: XAI_CONFIG.tokenUrl };
  }
  throw err;
}

Prevention

When it happens

Trigger: The discovery JSON's authorization_endpoint or token_endpoint is present but not a parseable URL — e.g. 'x.ai/oauth/authorize' (no scheme), 'not-a-url', relative paths, or strings containing spaces/control characters.

Common situations: Misconfigured reverse proxy returning HTML or a truncated string, a modified XAI_CONFIG.discoveryUrl pointing at a mock or third-party mirror, or unit tests feeding malformed fixtures.

Related errors


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