Skip to content

Handle 403 and 429 from /api/ai/triage in the UI#18

Merged
lai3d merged 1 commit into
mainfrom
claude/ui-triage-403-429
May 20, 2026
Merged

Handle 403 and 429 from /api/ai/triage in the UI#18
lai3d merged 1 commit into
mainfrom
claude/ui-triage-403-429

Conversation

@lai3d
Copy link
Copy Markdown
Owner

@lai3d lai3d commented May 20, 2026

Summary

Backend PR #13 made /api/ai/triage admin/operator-only (403 for readonly users and per-VPS agent API-keys), and PR #16 added a per-user LLM rate limit (429 with Retry-After). The web UI was unprepared on both fronts:

  • The "Ask AI" button on the VPS detail page rendered for every authenticated role, sending readonly users straight into a 403.
  • The AiTriageDialog caught any error from the mutation and showed a single generic red "Request failed" banner — no distinction between auth, rate-limit, and real failures.

This PR fixes both in one place.

Changes

  • sigma-web/src/pages/VpsDetail.tsx — gate the "Ask AI" button behind the existing canMutate (admin | operator), matching the other mutation buttons.
  • sigma-web/src/components/AiTriageDialog.tsx — branch on axios.isAxiosError(error) + error.response?.status:
    • 429: read error.response.headers['retry-after'] and render an amber panel: "You've hit your LLM rate limit. Try again in X minutes Y seconds" (uses minutes when >= 60s, plain seconds otherwise). Amber because it's a recoverable hint, mirroring the existing available: false degraded view.
    • 403: amber panel: "Your role doesn't permit AI triage. Ask an admin or operator." This branch still matters even with the button hidden, because readonly users can still reach the standalone /ai-triage page from the sidebar.
    • Other errors: keep the existing red "Request failed" banner unchanged.

Test plan

  • As a readonly user, visit /vps/:id — the "Ask AI" button is hidden.
  • As admin / operator, the "Ask AI" button is still shown (and not when the VPS is deleted).
  • As a readonly user, open /ai-triage from the sidebar and submit — dialog renders the amber 403 panel ("your role doesn't permit AI triage"), not a red banner.
  • As an operator, exhaust the per-user LLM rate limit and submit — dialog renders the amber 429 panel showing the formatted Retry-After duration. Verify the minutes/seconds formatting boundary (e.g. 30s -> "30 seconds", 90s -> "1 minute 30 seconds", 120s -> "2 minutes").
  • Force a non-403/429 error (e.g. network failure) — the existing red "Request failed" banner still appears.
  • npm run build in sigma-web/ passes with no TS errors.

Refs #13 (403), #16 (429).

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Backend PR #13 made /api/ai/triage admin/operator-only (403 for readonly
and per-VPS agent keys), and PR #16 added a per-user LLM rate limit
(429 with Retry-After). The web UI silently routed both into a generic
red "Request failed" banner and still rendered the "Ask AI" button on
the VPS detail page for every authenticated role.

VpsDetail: gate the "Ask AI" button behind canMutate (admin|operator),
matching the other mutation actions on the page.

AiTriageDialog: branch on axios error status. 429 surfaces the
Retry-After header as "Try again in X minutes Y seconds" in an amber
panel (it's a recoverable hint, not a hard error). 403 renders a
friendly "your role doesn't permit AI triage" amber panel — readonly
users can still reach the standalone /ai-triage page from the sidebar,
so the dialog still needs this branch. Other errors keep the existing
red banner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lai3d lai3d merged commit eb39fa7 into main May 20, 2026
1 check passed
@lai3d lai3d deleted the claude/ui-triage-403-429 branch May 20, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant