{"record":{"id":"bb637f652c2725cb","repo":"nextai-translator/nextai-translator","slug":"invalid-api-key-bb637f","errorCode":null,"errorMessage":"Invalid API key","messagePattern":"Invalid API key","errorType":"http","errorClass":"Error","httpStatus":401,"severity":"error","filePath":"src/common/engines/litellm.ts","lineNumber":37,"sourceCode":"            return []\n        }\n        const url = urlJoin(apiURL, '/v1/models')\n        const fetcher = getUniversalFetch()\n        const headers: Record<string, string> = {\n            'Content-Type': 'application/json',\n        }\n        // A LiteLLM proxy usually requires a virtual/master key, but some run\n        // without auth, so only send the header when a key is configured.\n        if (apiKey) {\n            headers['Authorization'] = `Bearer ${apiKey}`\n        }\n        const response = await fetcher(url, {\n            method: 'GET',\n            headers,\n        })\n        if (response.status !== 200) {\n            if (response.status === 401 || response.status === 403) {\n                throw new Error('Invalid API key')\n            }\n            if (response.status === 404) {\n                throw new Error('Invalid API URL')\n            }\n            throw new Error(`Failed to list models: ${response.statusText}`)\n        }\n        const json = await response.json()\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n        return json.data.map((model: any) => {\n            return {\n                id: model.id,\n                name: model.id,\n            }\n        })\n    }\n\n    async getAPIModel(): Promise<string> {\n        const settings = await getSettings()","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/nextai-translator/nextai-translator/blob/f57537ee4ad8ec654cb726b9907bab7a1f82601d/src/common/engines/litellm.ts#L19-L55","documentation":"LiteLLM's listModels() maps HTTP 401 or 403 from the GET /models request to 'Invalid API key'. The virtual-key/Bearer token passed in the Authorization header was rejected by the LiteLLM proxy. Called via the public models() wrapper.","triggerScenarios":"Calling listModels() (or models()) on the LiteLLM engine when the proxy responds 401/403: the LiteLLM virtual key is wrong, expired, deleted, lacks model access, or no Authorization header was sent at all.","commonSituations":"Admin rotated/deleted a virtual key in the LiteLLM proxy UI; key typed with trailing whitespace; key created without permissions for the /models route; pointing at a proxy that requires auth while settings have an empty key field.","solutions":["Verify/generate a valid virtual key in the LiteLLM proxy admin UI and re-enter it in settings","Test the key directly: curl -H 'Authorization: Bearer <key>' <proxy>/v1/models","Confirm the key has access to the models route and the allowed-models list is not empty","Check the configured LiteLLM proxy base URL matches your deployment"],"exampleFix":"// before\nconst models = await litellm.models('') // empty key → 401\n// after\nconst key = settings.litellmApiKey?.trim()\nif (!key) throw new Error('Set your LiteLLM virtual key in settings')\nconst models = await litellm.models(key)","handlingStrategy":"validation","validationCode":"const key = apiKey?.trim()\nif (!key) throw new Error('LiteLLM virtual key missing')\nconst probe = await fetch(`${proxyBase}/v1/models`, { headers: { Authorization: `Bearer ${key}` } })\nif (probe.status === 401 || probe.status === 403) throw new Error('LiteLLM key rejected by proxy')","typeGuard":"function isNonEmptyKey(k: unknown): k is string {\n  return typeof k === 'string' && k.trim().length > 0\n}","tryCatchPattern":"try {\n  const models = await litellm.models(apiKey)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Invalid API key') {\n    openSettings('LiteLLM proxy rejected the key — regenerate a virtual key in the proxy UI')\n  } else { throw e }\n}","preventionTips":["Regenerate and re-enter the virtual key whenever proxy admins rotate keys","Trim keys on input","Grant the key access to the models route in the proxy config","Smoke-test the key with curl against /v1/models before saving"],"tags":["authentication","api-key","http-401","http-403","litellm"],"backgroundTag":"invalid-api-key","analyzedSha":"f57537ee4ad8ec654cb726b9907bab7a1f82601d","analyzedAt":"2026-08-31T11:24:08.384Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}