feat(web): surface 429 rate limiting with standard retry wording #312

Merged
Claude-Run merged 1 commit from feat/BUNYIP-314-surface-429-retry-wording into main 2026-07-01 18:03:44 +02:00
Member

bunyip-web previously discarded the retry information on a 429: ApiError never read the Retry-After header or details.retry_after body field, user_message() returned the raw dunite-core sentence, and the onboarding auto-send swallowed the result entirely, so a throttled user saw nothing.

Capture the Retry-After header (whole seconds) on every Resp and add retry_after: Option<u64> to ApiError, populated in error_from from the header first with an error.details.retry_after envelope fallback. Add humanize_retry_after producing standard copy ("in about a minute" / "in about N minutes" / "in about an hour") and compose two 429 messages: user_message() returns the generic "Too many attempts. Please try again {humanized}." while verification_message() returns the verification-specific "You have requested too many verification emails. Please try again {humanized}." and falls back to user_message() for any non-429 error.

Point the resend-verification button (settings_resend_verification) and the onboarding auto-send at verification_message(); the onboarding page now renders a throttled auto-send's message in its error slot instead of silently doing nothing. Non-429 handling is unchanged.

Unit tests cover the humanizer boundaries (59s, 60s, 3599s, 3600s), header-vs-details precedence, and that non-429 errors keep their raw message.

#BUNYIP-314

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

bunyip-web previously discarded the retry information on a 429: `ApiError` never read the `Retry-After` header or `details.retry_after` body field, `user_message()` returned the raw dunite-core sentence, and the onboarding auto-send swallowed the result entirely, so a throttled user saw nothing. Capture the `Retry-After` header (whole seconds) on every `Resp` and add `retry_after: Option<u64>` to `ApiError`, populated in `error_from` from the header first with an `error.details.retry_after` envelope fallback. Add `humanize_retry_after` producing standard copy ("in about a minute" / "in about N minutes" / "in about an hour") and compose two 429 messages: `user_message()` returns the generic "Too many attempts. Please try again {humanized}." while `verification_message()` returns the verification-specific "You have requested too many verification emails. Please try again {humanized}." and falls back to `user_message()` for any non-429 error. Point the resend-verification button (`settings_resend_verification`) and the onboarding auto-send at `verification_message()`; the onboarding page now renders a throttled auto-send's message in its error slot instead of silently doing nothing. Non-429 handling is unchanged. Unit tests cover the humanizer boundaries (59s, 60s, 3599s, 3600s), header-vs-details precedence, and that non-429 errors keep their raw message. #BUNYIP-314 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(web): surface 429 rate limiting with standard retry wording
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 24s
Check / fmt + clippy + build + tests (pull_request) Successful in 11m7s
Create release / Create release from merged PR (pull_request) Has been skipped
3d8aa06a34
bunyip-web previously discarded the retry information on a 429: `ApiError` never read the `Retry-After` header or `details.retry_after` body field, `user_message()` returned the raw dunite-core sentence, and the onboarding auto-send swallowed the result entirely, so a throttled user saw nothing.

Capture the `Retry-After` header (whole seconds) on every `Resp` and add `retry_after: Option<u64>` to `ApiError`, populated in `error_from` from the header first with an `error.details.retry_after` envelope fallback. Add `humanize_retry_after` producing standard copy ("in about a minute" / "in about N minutes" / "in about an hour") and compose two 429 messages: `user_message()` returns the generic "Too many attempts. Please try again {humanized}." while `verification_message()` returns the verification-specific "You have requested too many verification emails. Please try again {humanized}." and falls back to `user_message()` for any non-429 error.

Point the resend-verification button (`settings_resend_verification`) and the onboarding auto-send at `verification_message()`; the onboarding page now renders a throttled auto-send's message in its error slot instead of silently doing nothing. Non-429 handling is unchanged.

Unit tests cover the humanizer boundaries (59s, 60s, 3599s, 3600s), header-vs-details precedence, and that non-429 errors keep their raw message.

#BUNYIP-314

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Run deleted branch feat/BUNYIP-314-surface-429-retry-wording 2026-07-01 18:03:44 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!312
No description provided.