{"record":{"id":"e99514c6cec1c9c6","repo":"decolua/9router","slug":"missing-api-key-e99514","errorCode":null,"errorMessage":"Missing API key","messagePattern":"Missing API key","errorType":"validation","errorClass":null,"httpStatus":401,"severity":"error","filePath":"src/sse/handlers/fetch.js","lineNumber":54,"sourceCode":"  const targetUrl = body.url;\n  const format = body.format;\n  const maxCharacters = body.max_characters;\n\n  log.request(\"POST\", `${reqUrl.pathname} | ${providerInput}`);\n\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  }","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/fetch.js#L36-L72","documentation":"HTTP 401 returned by handleFetch when requireApiKey is enabled in gateway settings and no API key was supplied with the request. extractApiKey finds no key in headers (or query), so the handler rejects before validating anything else.","triggerScenarios":"POST to the fetch endpoint with no Authorization header (and no api key query param) while the router has requireApiKey=true.","commonSituations":"Key enforcement enabled after clients were built without auth; local scripts hitting a hardened remote gateway; test harnesses that skip auth headers; headers stripped by an intermediary proxy.","solutions":["Add `Authorization: Bearer <gateway-api-key>` to the request","Copy the exact key from dashboard settings into the client config/env","If the gateway is local-only, disable requireApiKey in settings","Check that your reverse proxy is not stripping the Authorization header"],"exampleFix":"// before\nfetch('http://gw/v1/fetch', { method: 'POST', body })\n// after\nfetch('http://gw/v1/fetch', { method: 'POST', headers: { Authorization: `Bearer ${process.env.ROUTER_API_KEY}` }, body })","handlingStrategy":"type-guard","validationCode":"const apiKey = process.env.ROUTER_API_KEY;\nif (!apiKey) throw new Error('ROUTER_API_KEY is required when the gateway enforces requireApiKey');","typeGuard":"function isAuthedRequest(init) {\n  const h = new Headers(init.headers);\n  const auth = h.get('authorization') ?? '';\n  return /^Bearer\\s+\\S+$/.test(auth);\n}","tryCatchPattern":"const res = await post('/v1/fetch', body);\nif (res.status === 401 && (await res.text()).includes('Missing API key')) {\n  throw new Error('Gateway requires an API key — attach Authorization: Bearer <key>');\n}","preventionTips":["Attach the auth header via a shared request wrapper so it is never forgotten","Keep a health-check script that verifies auth before batch runs","Note in team docs whether each environment enforces requireApiKey"],"tags":["auth","api-key","http-401"],"backgroundTag":"missing-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}