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 <noreply@anthropic.com>
This commit is contained in:
Scott Werner 2026-10-27 13:59:04 -04:00
parent 16abf08fa7
commit fdfa016c3a
2 changed files with 11 additions and 1 deletions

View file

@ -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]"))

View file

@ -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." };
}