fix: classify quota errors as usage_limit instead of auth (#25676)

Fixes CODAGT-484.

- Removed "quota", "billing", "insufficient_quota", "payment required"
from `authStrongPatterns`
- Added `usageLimitPatterns` slice with those patterns
- Added `usageLimitMatch` signal and rule between overloaded and
authStrong in priority
- Added terminal/retry messages for `ChatErrorKindUsageLimit`
- Simplified auth message (removed billing reference)
- Frontend: conditional `!usageLimitStatus.provider` guard on the "View
Usage" Alert
- Added `TestClassify_UsageLimitBeatsAuth` with 5 cases including real
production OpenAI error
- Added `ProviderQuotaExceeded` story asserting no "View Usage" link and
correct `ChatStatusCallout` rendering

> Generated with [Coder Agents](https://coder.com/agents)
This commit is contained in:
Cian Johnston
2026-05-27 09:45:36 +01:00
committed by GitHub
parent e32be68687
commit 6acfe6c835
7 changed files with 150 additions and 13 deletions
@@ -74,6 +74,40 @@ export const UsageLimitExceeded: Story = {
},
};
/**
* Provider quota errors use the standard ChatStatusCallout instead of the
* "View Usage" CTA (which links to Coder's analytics, not the provider's
* billing page).
*/
export const ProviderQuotaExceeded: Story = {
args: {
...defaultArgs,
liveStatus: buildLiveStatus({
streamError: {
kind: "usage_limit",
message:
"The usage quota for OpenAI has been exceeded. Check the billing and quota settings for the provider account.",
provider: "openai",
retryable: false,
},
}),
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(
canvas.getByText(/usage quota for openai has been exceeded/i),
).toBeVisible();
// The "View Usage" link must NOT appear for provider-originated quota errors.
expect(
canvas.queryByRole("link", { name: /view usage/i }),
).not.toBeInTheDocument();
// Should render ChatStatusCallout instead.
expect(
canvas.getByRole("heading", { name: /usage limit reached/i }),
).toBeVisible();
},
};
/** Provider failures keep the footer-level terminal callout and status link. */
export const TerminalOverloadedError: Story = {
args: {
@@ -90,7 +90,7 @@ export const LiveStreamTailContent = ({
mcpServers={mcpServers}
/>
)}
{usageLimitStatus ? (
{usageLimitStatus && !usageLimitStatus.provider ? (
<Alert
severity="info"
actions={