decolua/9router · error

Missing API key

Error message

Missing API key

What it means

requireApiKey is enabled in gateway settings but the embeddings request carried no API key. extractApiKey(request) returned nothing, so handleEmbeddings returns 401 'Missing API key' — identical policy to the chat handler.

Source

Thrown at src/sse/handlers/embeddings.js:58

  const url = new URL(request.url);
  const modelStr = body.model;

  log.request("POST", `${url.pathname} | ${modelStr}`);

  // Log API key (masked)
  const apiKey = extractApiKey(request);
  if (apiKey) {
    log.debug("AUTH", `API Key: ${log.maskKey(apiKey)}`);
  } 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("EMBEDDINGS", "Missing model");
    return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing model");
  }

  if (!body.input) {
    log.warn("EMBEDDINGS", "Missing input");
    return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing required field: input");
  }

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Send Authorization: Bearer <9router-api-key> (or x-api-key) with embeddings requests.
  2. Set the apiKey option in your embedding client/driver, not just baseURL.
  3. Disable requireApiKey in dashboard settings if this is a trusted local-only deployment.
  4. Verify no intermediary strips auth headers before the gateway.

Example fix

// before
const embedder = new OpenAIEmbeddings({ baseURL: 'http://localhost:20128/v1' });

// after
const embedder = new OpenAIEmbeddings({
  baseURL: 'http://localhost:20128/v1',
  apiKey: process.env.NINE_ROUTER_API_KEY
});
Defensive patterns

Strategy: validation

Validate before calling

const apiKey = process.env.NINE_ROUTER_API_KEY;
if (!apiKey) throw new Error('embeddings client requires an API key (requireApiKey is on)');
opts.headers = { ...opts.headers, Authorization: `Bearer ${apiKey}` };

Type guard

function requestHasKey(opts) {
  const h = opts.headers || {};
  return Boolean(h['x-api-key']) || /^Bearer\s+\S+/.test(h['Authorization'] || h['authorization'] || '');
}

Try / catch

const res = await fetch(embedUrl, opts);
if (res.status === 401 && (await res.text()).includes('Missing API key')) {
  throw new Error('Add Authorization: Bearer <9router key> to embeddings requests');
}

Prevention

When it happens

Trigger: POST /v1/embeddings with settings.requireApiKey=true and no Authorization/x-api-key header on the request.

Common situations: Embedding clients (custom scripts, LangChain embeddings wrappers) configured with baseURL only and no key because embeddings calls were previously unauthenticated; enforcement toggled on after deployment; a reverse proxy stripping the Authorization header.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


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