decolua/9router · error

Invalid API key

Error message

Invalid API key

What it means

API-key enforcement is on (requireApiKey=true), a key was supplied, but isValidApiKey rejected it. The handler returns 401 'Invalid API key'. The gateway validates the presented key against keys stored in its settings/database.

Source

Thrown at src/sse/handlers/chat.js:73

  const authHeader = request.headers.get("Authorization");
  const apiKey = extractApiKey(request);
  if (authHeader && apiKey) {
    const masked = log.maskKey(apiKey);
    log.debug("AUTH", `API Key: ${masked}`);
  } else {
    log.debug("AUTH", "No API key provided (local mode)");
  }

  // Enforce API key if enabled in settings
  const settings = await getSettings();
  if (settings.requireApiKey) {
    if (!apiKey) {
      log.warn("AUTH", "Missing API key (requireApiKey=true)");
      return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Missing API key");
    }
    const valid = await isValidApiKey(apiKey);
    if (!valid) {
      log.warn("AUTH", "Invalid API key (requireApiKey=true)");
      return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Invalid API key");
    }
  }

  if (!modelStr) {
    log.warn("CHAT", "Missing model");
    return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing model");
  }

  // Bypass naming/warmup requests before combo rotation to avoid wasting rotation slots
  const userAgent = request?.headers?.get("user-agent") || "";
  const bypassResponse = handleBypassRequest(body, modelStr, userAgent, !!settings.ccFilterNaming);
  if (bypassResponse) return bypassResponse.response || bypassResponse;

  const requiredCapabilities = detectRequiredCapabilities(body);

  // Check if model is a combo (has multiple models with fallback)
  const comboModels = await getComboModels(modelStr);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Open the 9Router dashboard and copy the exact current API key, then update the client env/config.
  2. Check for stray whitespace/quotes in the env var: API_KEY="abc" in .env often embeds literal quotes.
  3. Make sure you are using a 9Router-issued key, not an upstream provider key.
  4. If the key was rotated, restart the client so it reloads the new value.

Example fix

// before (.env)
OPENAI_API_KEY="sk-abc123"   # literal quotes end up in the value

// after (.env)
API_KEY=sk-abc123            # correct 9Router key, no quotes/whitespace
Defensive patterns

Strategy: validation

Validate before calling

const apiKey = (process.env.NINE_ROUTER_API_KEY || '').trim();
if (!apiKey) throw new Error('missing 9router api key');
if (/^["'].*["']$/.test(process.env.NINE_ROUTER_API_KEY)) throw new Error('api key env var contains literal quotes');

Type guard

function looksLikeGatewayKey(key) {
  return typeof key === 'string' && key.trim().length > 0 && !/["']/.test(key);
}

Try / catch

const res = await fetch(url, { headers });
if (res.status === 401 && (await res.text()).includes('Invalid API key')) {
  throw new Error('API key rejected by 9Router — re-copy the key from the dashboard');
}

Prevention

When it happens

Trigger: POST to /v1/chat/completions with Authorization: Bearer <key> where <key> does not match any key registered in the 9Router dashboard while settings.requireApiKey=true.

Common situations: Typo or stale key in the client env (pointing at an upstream provider key like OPENAI_API_KEY instead of a 9Router key); key rotated/regenerated in the dashboard but old value cached in .env; extra whitespace or quotes in the env var; multiple environments sharing one config.

Understand the failure class

Related errors


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