From fdfa016c3a873c39ea2c54886e004844974a7269 Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Tue, 27 Oct 2026 13:59:04 -0400 Subject: [PATCH] Don't blame the API key for an access_denied refusal Every 403 was reported as a rejected API key, so a user whose key works but whose account isn't allowed the model or region would rotate a good key. Only lithos-llm's authentication kind or a 401 gets the API-key wording; access_denied and other 403s say access was denied. Co-Authored-By: Claude Opus 5.5 --- apps/fabro-web/app/routes/run-detail/failure.test.ts | 4 ++++ apps/fabro-web/app/routes/run-detail/failure.ts | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/fabro-web/app/routes/run-detail/failure.test.ts b/apps/fabro-web/app/routes/run-detail/failure.test.ts index f489e7b76..c5d575ab1 100644 --- a/apps/fabro-web/app/routes/run-detail/failure.test.ts +++ b/apps/fabro-web/app/routes/run-detail/failure.test.ts @@ -70,6 +70,10 @@ describe("buildRunFailureView", () => { const headline = (message: string) => view(failedRun({ message }))?.headline; expect(headline("model request failed (authentication): provider anthropic invalid x-api-key [provider anthropic, status 401, code authentication_error]")) .toBe("Anthropic rejected the API key"); + expect(headline("model request failed (access_denied): provider openai The model is not available to your organization [provider openai, status 403, code model_not_found]")) + .toBe("OpenAI denied access to the request"); + expect(headline("model request failed (authentication): provider acme forbidden [provider acme, status 403, code forbidden]")) + .toBe("acme rejected the API key"); expect(headline("model request failed (rate_limit): provider openai Too many requests [provider openai, status 429, code rate_limit_exceeded]")) .toBe("OpenAI rate-limited the request"); expect(headline("model request failed (server): provider anthropic Overloaded [provider anthropic, status 529, code overloaded_error]")) diff --git a/apps/fabro-web/app/routes/run-detail/failure.ts b/apps/fabro-web/app/routes/run-detail/failure.ts index 35a76d081..3f8080d65 100644 --- a/apps/fabro-web/app/routes/run-detail/failure.ts +++ b/apps/fabro-web/app/routes/run-detail/failure.ts @@ -151,9 +151,15 @@ function requestAdvice({ kind, status, code }: ModelRequest, message: string, wh nextStep: `Add credit or raise the spend limit with ${who}, then ${RETRY}.`, }; } - if (kind === "authentication" || status === 401 || status === 403) { + if (kind === "authentication" || status === 401) { return { headline: `${who} rejected the API key`, nextStep: `Check the key under Settings → Secrets, then ${RETRY}.` }; } + if (kind === "access_denied" || status === 403) { + return { + headline: `${who} denied access to the request`, + nextStep: `The key works, but ${who} doesn't allow this account to make the call. ${who}'s message, under Details, says why.`, + }; + } if (kind === "rate_limit" || status === 429) { return { headline: `${who} rate-limited the request`, nextStep: "This is usually temporary. Choose Retry from Actions in a few minutes." }; }