{"record":{"id":"b4a1c0178f7bf245","repo":"janhq/jan","slug":"access-forbidden-check-your-api-key-permissions-f","errorCode":null,"errorMessage":"Access forbidden: Check your API key permissions for ${provider.provider}","messagePattern":"Access forbidden: Check your API key permissions for (.+?)","errorType":"http","errorClass":"Error","httpStatus":403,"severity":"error","filePath":"web-app/src/services/providers/tauri.ts","lineNumber":208,"sourceCode":"\n        lastStatus = response.status\n        lastStatusText = response.statusText\n\n        if (\n          [401, 403, 429].includes(response.status) &&\n          ki < keyAttempts.length - 1\n        ) {\n          continue\n        }\n\n        if (!response.ok) {\n          if (response.status === 401) {\n            throw new Error(\n              `Authentication failed: API key is required or invalid for ${provider.provider}`\n            )\n          }\n          if (response.status === 403) {\n            throw new Error(\n              `Access forbidden: Check your API key permissions for ${provider.provider}`\n            )\n          }\n          if (response.status === 404) {\n            throw new Error(\n              `Models endpoint not found for ${provider.provider}. Check the base URL configuration.`\n            )\n          }\n          throw new Error(\n            `Failed to fetch models from ${provider.provider}: ${response.status} ${response.statusText}`\n          )\n        }\n\n        const data = await response.json()\n\n        if (data.data && Array.isArray(data.data)) {\n          return data.data\n            .map((model: { id: string }) => model.id)","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/services/providers/tauri.ts#L190-L226","documentation":"Thrown by TauriProviderService.fetchModelsFromProvider (providers/tauri.ts:208) when the GET ${base_url}/models request returns 403 after key rotation is exhausted. Unlike 401 (key missing/invalid), 403 means the key authenticated but lacks permission to list models for this account/workspace.","triggerScenarios":"The API key is valid (passed auth) but the associated account/role is not permitted to call /models: restricted scope, read-only key without model-list permission, organization SSO required, or plan tier restriction.","commonSituations":"Key created with narrow scope (inference-only, no catalog read); enterprise provider requiring an admin-granted role; trial account without model-list access; key belongs to a different org/project than the base_url.","solutions":["In the provider dashboard, assign the key a role/scope that includes model-list (read) permission.","Verify the key belongs to the same organization/project as the configured base_url.","If SSO is enforced, complete SSO and regenerate the key.","Switch to an account/plan that permits listing models."],"exampleFix":"// before\nif (response.status === 403) {\n  throw new Error(`Access forbidden: Check your API key permissions for ${provider.provider}`)\n}\n// after: keep the message but also surface the upstream body if present\nif (response.status === 403) {\n  const detail = await safeReadError(response)\n  throw new Error(`Access forbidden for ${provider.provider}: ${detail ?? 'key lacks model-list scope'}`)\n}","handlingStrategy":"try-catch","validationCode":"function keyHasReadScope(scopes: string[]): boolean {\n  return scopes.some(s => /model|read|catalog/i.test(s))\n}\n// when the provider exposes key scopes, validate before the request","typeGuard":"function isForbiddenError(e: unknown, provider: string): boolean {\n  return e instanceof Error && /Access forbidden.*provider/i.test(e.message)\n}","tryCatchPattern":"try {\n  return await providerService.fetchModelsFromProvider(provider)\n} catch (e) {\n  if (e instanceof Error && /Access forbidden/.test(e.message)) {\n    toast.error(`The key for ${provider.provider} lacks model-list permission. Use a broader-scoped key.`)\n    return []\n  }\n  throw e\n}","preventionTips":["Use a key whose scope includes model-list/read, not inference-only.","Verify the key belongs to the same org/project as the base_url.","Complete SSO if the provider enforces it before issuing keys.","Distinguish 403 (permissions) from 401 (auth) in user messaging."],"tags":["provider","auth","authorization","http-status","permissions","tauri","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}