{"record":{"id":"2e9607d7c228ca8d","repo":"decolua/9router","slug":"invalid-api-key-2e9607","errorCode":null,"errorMessage":"Invalid API key","messagePattern":"Invalid API key","errorType":"validation","errorClass":null,"httpStatus":401,"severity":"error","filePath":"src/sse/handlers/fetch.js","lineNumber":59,"sourceCode":"\n  // Log API key (masked)\n  const apiKey = extractApiKey(request);\n  if (apiKey) {\n    log.debug(\"AUTH\", `API Key: ${log.maskKey(apiKey)}`);\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 (!providerInput || typeof providerInput !== \"string\") {\n    log.warn(\"FETCH\", \"Missing provider/model\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing required field: provider (or model)\");\n  }\n\n  if (!targetUrl || typeof targetUrl !== \"string\") {\n    log.warn(\"FETCH\", \"Missing url\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing required field: url\");\n  }\n\n  // Validate URL format\n  try {\n    new URL(targetUrl);\n  } catch {","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/fetch.js#L41-L77","documentation":"HTTP 401 returned by handleFetch when requireApiKey is enabled and the supplied key fails isValidApiKey — i.e. the key exists but does not match the gateway's configured key.","triggerScenarios":"POST to the fetch endpoint with an Authorization header whose bearer token is wrong, stale, or belongs to a different router instance while requireApiKey=true.","commonSituations":"Key rotated in the dashboard but client env var not updated; copying the upstream provider key instead of the gateway key; whitespace/newline in the env var; multiple gateway instances with different keys.","solutions":["Verify the key against dashboard settings and update the client env var","Trim whitespace around the key value in .env","Ensure you are calling the intended gateway instance/environment","Generate a fresh key in the dashboard and redeploy clients"],"exampleFix":"// before\nAuthorization: Bearer ${ROUTER_KEY.trim()} // value from stale env\n// after\n# .env updated with the current dashboard key, then restart\nAuthorization: Bearer ${ROUTER_API_KEY}","handlingStrategy":"validation","validationCode":"const key = (process.env.ROUTER_API_KEY ?? '').trim();\nif (!key || key.startsWith('sk-')) console.warn('Suspicious ROUTER_API_KEY: empty or looks like an upstream provider key');","typeGuard":"function looksLikeGatewayKey(k) {\n  return typeof k === 'string' && k.trim().length >= 8;\n}","tryCatchPattern":"const res = await post('/v1/fetch', body);\nif (res.status === 401 && (await res.text()).includes('Invalid API key')) {\n  console.error('Key rejected — re-sync ROUTER_API_KEY from dashboard settings and trim whitespace');\n}","preventionTips":["Re-sync the key env var whenever dashboard settings change","Trim env values; avoid copying with trailing newlines","Label the gateway key distinctly from provider keys","Rotate keys through a single config source"],"tags":["auth","api-key","http-401"],"backgroundTag":"invalid-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}