{"record":{"id":"f034cb0ba4d7b3e9","repo":"decolua/9router","slug":"missing-api-key","errorCode":null,"errorMessage":"Missing API key","messagePattern":"Missing API key","errorType":"validation","errorClass":null,"httpStatus":401,"severity":"error","filePath":"src/sse/handlers/chat.js","lineNumber":68,"sourceCode":"  const modelStr = body.model;\n\n  // Request summary is emitted as the unified \"▶\" line in chatCore (has fmt/thinking/account)\n\n  // Log API key (masked)\n  const authHeader = request.headers.get(\"Authorization\");\n  const apiKey = extractApiKey(request);\n  if (authHeader && apiKey) {\n    const masked = log.maskKey(apiKey);\n    log.debug(\"AUTH\", `API Key: ${masked}`);\n  } else {\n    log.debug(\"AUTH\", \"No API key provided (local mode)\");\n  }\n\n  // Enforce API key if enabled in settings\n  const settings = await getSettings();\n  if (settings.requireApiKey) {\n    if (!apiKey) {\n      log.warn(\"AUTH\", \"Missing API key (requireApiKey=true)\");\n      return errorResponse(HTTP_STATUS.UNAUTHORIZED, \"Missing API key\");\n    }\n    const valid = await isValidApiKey(apiKey);\n    if (!valid) {\n      log.warn(\"AUTH\", \"Invalid API key (requireApiKey=true)\");\n      return errorResponse(HTTP_STATUS.UNAUTHORIZED, \"Invalid API key\");\n    }\n  }\n\n  if (!modelStr) {\n    log.warn(\"CHAT\", \"Missing model\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing model\");\n  }\n\n  // Bypass naming/warmup requests before combo rotation to avoid wasting rotation slots\n  const userAgent = request?.headers?.get(\"user-agent\") || \"\";\n  const bypassResponse = handleBypassRequest(body, modelStr, userAgent, !!settings.ccFilterNaming);\n  if (bypassResponse) return bypassResponse.response || bypassResponse;","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/chat.js#L50-L86","documentation":"API-key enforcement is enabled in the gateway settings (requireApiKey=true) but no API key was found on the request. extractApiKey(request) looks at the Authorization header (Bearer token) and possibly x-api-key; when it returns nothing and enforcement is on, the handler returns 401 with 'Missing API key'.","triggerScenarios":"POST to /v1/chat/completions with settings.requireApiKey=true and no Authorization header (or no x-api-key header) on the request.","commonSituations":"User enabled 'require API key' in the dashboard after the client was already configured without a key; SDK default (e.g. OpenAI SDK) silently omitting the key because baseURL was set but apiKey left empty; calling the endpoint from a browser or script without auth headers.","solutions":["Add the key: set Authorization: Bearer <your-9router-api-key> (or x-api-key) on every request.","Configure your OpenAI SDK client with apiKey: 'your-9router-key' — never leave it empty when requireApiKey is on.","If local-only usage is intended, disable requireApiKey in the dashboard settings (Settings → API key enforcement).","Verify which header extractApiKey reads for your client and that a proxy is not stripping Authorization before it reaches the gateway."],"exampleFix":"// before\nconst client = new OpenAI({ baseURL: 'http://localhost:20128/v1' });\n\n// after\nconst client = new OpenAI({\n  baseURL: 'http://localhost:20128/v1',\n  apiKey: process.env.NINE_ROUTER_API_KEY\n});","handlingStrategy":"validation","validationCode":"const apiKey = process.env.NINE_ROUTER_API_KEY;\nif (!apiKey) throw new Error('NINE_ROUTER_API_KEY is not set; gateway has requireApiKey enabled');\nheaders['Authorization'] = `Bearer ${apiKey}`;","typeGuard":"function hasApiKey(headers) {\n  const auth = headers['authorization'] || '';\n  return /^Bearer\\s+\\S+/.test(auth) || Boolean(headers['x-api-key']);\n}","tryCatchPattern":"const res = await fetch(url, { headers });\nif (res.status === 401 && (await res.text()).includes('Missing API key')) {\n  throw new Error('Gateway requires an API key: set Authorization: Bearer <key>');\n}","preventionTips":["Check the dashboard's requireApiKey setting before deploying clients.","Configure apiKey in every SDK client, even for localhost.","Fail fast at startup if the key env var is absent.","Confirm proxies preserve the Authorization header."],"tags":["auth","http","unauthorized","api-key"],"backgroundTag":"missing-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}