{"record":{"id":"8c2775a0dc8685cf","repo":"can1357/oh-my-pi","slug":"mnemopi-remote-llm-request-unauthorized-401","errorCode":null,"errorMessage":"mnemopi remote LLM request unauthorized (401)","messagePattern":"mnemopi remote LLM request unauthorized \\(401\\)","errorType":"http","errorClass":"ProviderHttpError","httpStatus":401,"severity":"error","filePath":"packages/mnemopi/src/core/local-llm.ts","lineNumber":366,"sourceCode":"\tconst fetchImpl = options.fetch ?? fetch;\n\ttry {\n\t\t// withAuth re-resolves the key on 401 (force-refresh, then sibling\n\t\t// rotation) when the configured key is a resolver. An empty static key\n\t\t// attempts without an Authorization header (local/proxy setups).\n\t\tconst response = await withAuth(llmApiKey(), async key => {\n\t\t\tconst headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n\t\t\tif (key !== \"\") {\n\t\t\t\theaders.Authorization = `Bearer ${key}`;\n\t\t\t}\n\t\t\tconst res = await fetchWithRetry(`${baseUrl}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t\tsignal: AbortSignal.timeout(60000),\n\t\t\t\tfetch: fetchImpl,\n\t\t\t});\n\t\t\tif (res.status === 401) {\n\t\t\t\tthrow new ProviderHttpError(\"mnemopi remote LLM request unauthorized (401)\", 401, { headers: res.headers });\n\t\t\t}\n\t\t\treturn res;\n\t\t});\n\t\tif (!response.ok) {\n\t\t\treturn null;\n\t\t}\n\t\tconst data = (await response.json()) as {\n\t\t\tchoices?: Array<{ message?: { content?: unknown } }>;\n\t\t};\n\t\tconst content = data.choices?.[0]?.message?.content;\n\t\treturn typeof content === \"string\" ? content : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function localGgufAvailable(): false {\n\treturn false;","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/core/local-llm.ts#L348-L384","documentation":"callRemoteLlm sends a request to the configured remote LLM endpoint and maps an HTTP 401 response to this ProviderHttpError (status 401, carrying the response headers). It means the configured credentials were rejected by the remote provider — the server explicitly said the request is unauthorized.","triggerScenarios":"The Authorization header (or equivalent) built from the configured API key/token is missing, expired, revoked, or scoped to a different endpoint when callRemoteLlm performs its fetch with a 60s timeout; any remote LLM call where res.status === 401.","commonSituations":"Rotated or mistyped API key in mnemopi config; expired token after SSO/session rotation; pointing the remote endpoint at a provider that doesn't accept the configured key; environment variable not set in the deployment (CI, container).","solutions":["Verify the API key/token configured for the remote LLM endpoint is current and re-set it if rotated","Confirm the credential has access to the exact model/endpoint being called","Check that the env var holding the key is actually present in the runtime environment (container/CI often drops it)","Test the key against the endpoint with curl to isolate mnemopi config vs provider-side rejection"],"exampleFix":"// before\n{ \"remoteLlm\": { \"apiKey\": \"sk-stale-...\" } }\n// after\n{ \"remoteLlm\": { \"apiKey\": \"sk-current-...\" } }  // or set via env","handlingStrategy":"try-catch","validationCode":"const apiKey = process.env.MNEMOPI_REMOTE_LLM_API_KEY;\nif (!apiKey || apiKey.length < 8) {\n  throw new Error(\"remote LLM API key missing/short; set MNEMOPI_REMOTE_LLM_API_KEY before calling\");\n}","typeGuard":"function hasCredential(cfg: { apiKey?: unknown }): cfg is { apiKey: string } {\n  return typeof cfg.apiKey === \"string\" && cfg.apiKey.length > 0;\n}","tryCatchPattern":"try {\n  const res = await callRemoteLlm(req);\n  // ...\n} catch (err) {\n  if (err instanceof ProviderHttpError && err.status === 401) {\n    logger.error(\"remote LLM rejected credentials; refresh the API key\", { url: req.url });\n    return null; // or requeue after credential refresh\n  }\n  throw err;\n}","preventionTips":["Rotate and re-set API keys on a schedule; never hardcode them","Verify the key is present in the deployment environment (container/CI) before starting","Validate the key with a cheap authenticated endpoint at startup","Scope credentials to the exact endpoint/model being called"],"tags":["http-401","authentication","remote-llm","api-key"],"backgroundTag":"http-401-unauthorized","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}