decolua/9router · critical

xai discovery ${field} host ${host} is not on x.ai

Error message

xai discovery ${field} host ${host} is not on x.ai

What it means

After protocol validation, the hostname must be exactly 'x.ai' or a subdomain ending in '.x.ai' (case-insensitive). Any other host is rejected, defending against lookalike/phishing domains in discovery documents that could capture authorization codes and tokens.

Source

Thrown at src/lib/oauth/services/xai.js:43

export function validateOAuthEndpoint(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;
}

/**
 * Discover authorization + token endpoints. Cached process-wide.
 */
export async function discoverEndpoints() {
  if (cachedDiscovery) return cachedDiscovery;

  try {
    const res = await fetch(XAI_CONFIG.discoveryUrl, {
      headers: { Accept: "application/json" },
    });
    if (res.ok) {
      const data = await res.json();
      cachedDiscovery = {

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Point the field at a genuine x.ai host (https://x.ai/... or https://api.x.ai/... as applicable).
  2. If you route through a corporate gateway, don't override the OAuth endpoints — proxy at the network layer instead.
  3. Verify the discovery document was fetched over TLS from x.ai, since a foreign host here means the discovery source is wrong or compromised.

Example fix

// before
XAI_TOKEN_URL=https://auth.mycompany.com/xai/token
// after
XAI_TOKEN_URL=https://x.ai/api/oauth/token
Defensive patterns

Strategy: validation

Validate before calling

function isXaiHost(u) {
  try {
    const h = new URL(String(u).trim()).hostname.toLowerCase();
    return h === 'x.ai' || h.endsWith('.x.ai');
  } catch { return false; }
}
if (!isXaiHost(cfg.tokenUrl)) throw new Error('xAI endpoint host must be x.ai or *.x.ai');

Type guard

function isXaiEndpoint(u) {
  if (typeof u !== 'string') return false;
  try {
    const parsed = new URL(u.trim());
    if (parsed.protocol !== 'https:') return false;
    const h = parsed.hostname.toLowerCase();
    return h === 'x.ai' || h.endsWith('.x.ai');
  } catch { return false; }
}

Prevention

When it happens

Trigger: Discovery field points at e.g. https://xai.example.com/..., https://x.ai.evil.io/... (endsWith('.x.ai') is false for this because host is 'x.ai.evil.io'), or a mirror domain like https://x-ai.com/.

Common situations: Man-in-the-middle or malicious discovery responses; teams proxying xAI through their own domain; typos in manually configured endpoints.

Related errors


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