{"record":{"id":"41fca4fdfeafaede","repo":"janhq/jan","slug":"no-api-key-configured-for-provider-provider-ad","errorCode":null,"errorMessage":"No API key configured for ${provider.provider}. Add one in Settings > Model Providers.","messagePattern":"No API key configured for (.+?)\\. Add one in Settings > Model Providers\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web-app/src/lib/model-factory.ts","lineNumber":791,"sourceCode":"        res.body?.cancel().catch(() => {})\n        continue\n      }\n      return res\n    }\n    throw new Error('API key rotation exhausted')\n  }\n}\n\n// An empty apiKey still puts an empty auth header on the wire, which upstreams\n// answer with misleading 401s (e.g. Anthropic's \"x-api-key header is\n// required\"). Fail here with an actionable message instead.\nfunction requireRemoteApiKey(\n  provider: ProviderObject,\n  keyChain: string[]\n): string {\n  const key = keyChain[0] ?? provider.api_key?.trim()\n  if (!key) {\n    throw new Error(\n      `No API key configured for ${provider.provider}. Add one in Settings > Model Providers.`\n    )\n  }\n  return key\n}\n\nfunction getRuntimeFetch(): typeof globalThis.fetch {\n  const maybeWindow = globalThis as typeof globalThis & {\n    __TAURI__?: unknown\n    __TAURI_INTERNALS__?: unknown\n  }\n  const hasTauriRuntime =\n    typeof maybeWindow.__TAURI__ !== 'undefined' ||\n    typeof maybeWindow.__TAURI_INTERNALS__ !== 'undefined'\n\n  return isPlatformTauri() && hasTauriRuntime\n    ? (httpFetch as typeof globalThis.fetch)\n    : globalThis.fetch","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/lib/model-factory.ts#L773-L809","documentation":"Thrown by requireRemoteApiKey when both the keyChain passed in and provider.api_key are empty/missing. The preceding comment explains the motivation: an empty key would still send an empty auth header, which upstreams answer with misleading 401s (e.g. Anthropic's 'x-api-key header is required'). This guard fails fast with an actionable message instead (model-factory.ts:791).","triggerScenarios":"A remote/cloud provider (not llamacpp/mlx/local) is selected for a chat, ModelFactory.createModel calls requireRemoteApiKey, and provider.api_key is undefined/empty while the keyChain array is empty or its first entry is falsy.","commonSituations":"First run with a cloud provider before the user entered a key; the key was saved to the OS keyring but keyring access was denied so keyChain came back empty; a stale provider object persisted in state after the user cleared credentials; whitespace-only key stored.","solutions":["Go to Settings > Model Providers, select the provider named in the message, and paste a valid API key.","If using the OS keyring, ensure the app has keychain access (macOS: allow in System Settings; Linux: unlock the keyring daemon).","Verify the key is not whitespace-only — the guard trims, so leading/trailing spaces produce the same failure.","Re-select the provider in the UI so the provider object is rehydrated with the freshly stored key."],"exampleFix":"// before\nconst key = keyChain[0] ?? provider.api_key?.trim()\nif (!key) throw new Error(`No API key configured for ${provider.provider}...`)\n// after: also clear empty keychain entries upstream so the UI disables Send\nif (!key) {\n  setErrorState({ code: 'MISSING_API_KEY', provider: provider.provider })\n  throw new Error(`No API key configured for ${provider.provider}. Add one in Settings > Model Providers.`)\n}","handlingStrategy":"validation","validationCode":"function hasRemoteApiKey(provider: ProviderObject, keyChain: string[]): boolean {\n  const k = keyChain[0] ?? provider.api_key?.trim()\n  return Boolean(k)\n}\n// before calling requireRemoteApiKey:\nif (!hasRemoteApiKey(provider, keyChain)) {\n  setNeedsKey(provider.provider)\n  return\n}","typeGuard":"function providerHasApiKey(p: ProviderObject): boolean {\n  return Boolean(p.api_key && p.api_key.trim().length > 0)\n}","tryCatchPattern":"try {\n  const key = requireRemoteApiKey(provider, keyChain)\n  // ... build model\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('No API key configured')) {\n    openSettings('model-providers', provider.provider)\n    return\n  }\n  throw e\n}","preventionTips":["Disable the Send button in the UI until provider.api_key is non-empty.","Trim and validate the key on save in Settings > Model Providers.","Treat empty-string keys as unset everywhere, not as a valid no-auth value.","Read keys from the keyring lazily and surface keyring-access errors distinctly."],"tags":["api-key","auth","configuration","validation","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}