Skip to main content

Rate limit errors show a specific, actionable message

ab-000147 · ai-chat-visibility.error-handling.rate-limit-message
Severity: highactive

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.

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

Detection

  • ID: ai-chat-visibility.error-handling.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:

    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

History