# Rate limit errors show a specific, actionable message

- **Pattern:** `ab-000147` (`ai-chat-visibility.error-handling.rate-limit-message`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000147
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000147)

## Why it matters

Rate limit errors are one of the most common failure modes in AI chat products — per-user quotas, per-IP throttles, and upstream provider 429s all route through the same code path. Showing users a generic "Something went wrong" message for a 429 makes the product look broken when in fact the user has simply hit a quota and the correct action is to wait or upgrade. This is a direct conversion lever: rate-limited users on a paid tier should see an upgrade prompt, not an error toast.

## Severity rationale

High because rate limits are an expected, recurring failure mode and generic errors convert revocable friction into churn.

## Remediation

In your API route at `app/api/chat/route.ts`, return structured 429 responses with a retry-after hint, and in `src/components/chat/ChatWindow.tsx` branch on `response.status === 429` with a specific user-facing message. For subscription-gated limits, surface an upgrade CTA.

```tsx
if (response.status === 429) {
  const { retryAfter } = await response.json();
  setError(`Rate limit reached. Try again in ${retryAfter} seconds.`);
}
```

## Detection

- **ID:** `rate-limit-message`
- **Severity:** `high`
- **What to look for:** Check how HTTP 429 responses are handled from the AI API route. Enumerate all error handler branches and classify whether any distinguish 429 from other status codes. When a user hits a rate limit — either the application's own per-user limits or the upstream AI provider's limits — the error message should tell them specifically what happened and what to do. A generic "Something went wrong" message is insufficient for rate limit scenarios.
- **Pass criteria:** A 429 response from the AI API route triggers a specific UI message that communicates the rate limit context. The message must include at least 1 of: a retry time estimate, a limit quantity, or an upgrade prompt (e.g., "You've reached your message limit for today. Limits reset at midnight.").
- **Fail criteria:** 429 responses are handled by the same generic error handler as all other errors, showing only "Something went wrong" without any rate-limit-specific context.
- **Do NOT pass when:** The API route returns a 429 status code but the frontend error handler does not check `response.status === 429` — a catch-all error handler that displays the same message for all errors is NOT a pass.
- **Skip (N/A) when:** The application has no rate limiting on the chat endpoint. Signal: no rate limiting logic detected in the API route or middleware files.
- **Detail on fail:** `"Rate limit (429) responses handled by generic error catch — no specific messaging or retry guidance shown to users"`
- **Remediation:** In your API route at `app/api/chat/route.ts`, return structured 429 responses. On the frontend in `src/components/chat/ChatWindow.tsx`, check status:

  ```tsx
  if (response.status === 429) {
    const { retryAfter } = await response.json();
    setError(`Rate limit reached. Try again in ${retryAfter} seconds.`);
  }
  ```

  For subscription-based limits, include upgrade prompts.

Taxons: error-resilience, user-experience

HTML version: https://auditbuffet.com/patterns/ab-000147
