{"record":{"id":"3c415ed41fa6e17d","repo":"decolua/9router","slug":"xai-discovery-field-is-empty-3c415e","errorCode":null,"errorMessage":"xai discovery ${field} is empty","messagePattern":"xai discovery (.+?) is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/xai.js","lineNumber":28,"sourceCode":" * xAI (Grok) OAuth Service\n *\n * Source of truth: router-for-me/CLIProxyAPI internal/auth/xai/xai.go\n *\n * Flow:\n *  1. Discover endpoints from `${XAI_ISSUER}/.well-known/openid-configuration`\n *  2. Bind loopback server on 127.0.0.1:56121, path /callback\n *  3. PKCE S256 with 96-byte verifier\n *  4. Exchange code with form-urlencoded body\n *  5. id_token email decode (no signature verify, mirrors Go)\n */\n\nconst BASE64_BLOCK_SIZE = 4;\n\nlet cachedDiscovery = null;\n\nexport function validateOAuthEndpoint(rawUrl, field) {\n  const value = String(rawUrl || \"\").trim();\n  if (!value) throw new Error(`xai discovery ${field} is empty`);\n\n  let parsed;\n  try {\n    parsed = new URL(value);\n  } catch (err) {\n    throw new Error(`xai discovery ${field} is invalid: ${err.message}`);\n  }\n\n  if (parsed.protocol !== \"https:\") {\n    throw new Error(`xai discovery ${field} must use https: ${value}`);\n  }\n\n  const host = parsed.hostname.toLowerCase().trim();\n  if (host !== \"x.ai\" && !host.endsWith(\".x.ai\")) {\n    throw new Error(`xai discovery ${field} host ${host} is not on x.ai`);\n  }\n\n  return value;","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/xai.js#L10-L46","documentation":"validateOAuthEndpoint in the xAI OAuth service validates a discovery URL field (e.g. authorization or token endpoint obtained from xAI's discovery document). It throws this when the raw value is missing, empty, or whitespace-only after trimming. Discovery-driven flows cannot proceed without concrete endpoint URLs.","triggerScenarios":"discoverEndpoints receives a discovery document where the expected field is absent/empty — e.g. xAI's well-known response omits a field, or config/env supplying the URL is unset ('' or undefined).","commonSituations":"Missing XAI_* env vars; xAI changing its OIDC discovery document shape; a partial/cached discovery document stored from a failed fetch.","solutions":["Check which 'field' the error names and set that URL explicitly in config/env.","Re-fetch the discovery document from https://x.ai/.well-known/... and confirm the field is present.","Clear any stale cached discovery data (the service caches discovery results).","Verify you're not passing an empty string where a URL constant was intended."],"exampleFix":"// before\nconst endpoints = discoverEndpoints({});\n// after\nconst endpoints = discoverEndpoints({ authorizationUrl: process.env.XAI_AUTH_URL, tokenUrl: process.env.XAI_TOKEN_URL });","handlingStrategy":"validation","validationCode":"const required = { authorizationUrl: cfg.authorizationUrl, tokenUrl: cfg.tokenUrl };\nfor (const [k, v] of Object.entries(required)) {\n  if (!v || !String(v).trim()) throw new Error(`xAI config: ${k} is not set`);\n}","typeGuard":"function hasEndpoint(u) {\n  return typeof u === 'string' && u.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Validate all XAI_* env vars at startup, fail fast before any OAuth call.","Provide defaults or explicit startup errors for unset endpoint config.","Re-fetch discovery from x.ai when a field comes back empty; clear stale caches."],"tags":["oauth","xai","validation","config"],"backgroundTag":"missing-oauth-params","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}