Skip to content

feat(api): retry Spotify 429s with backoff - #413

Open
chiptus wants to merge 6 commits into
mainfrom
fix-411/spotify-429-retry
Open

feat(api): retry Spotify 429s with backoff#413
chiptus wants to merge 6 commits into
mainfrom
fix-411/spotify-429-retry

Conversation

@chiptus

@chiptus chiptus commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Adds retry-with-backoff for Spotify API rate limits, honoring Retry-After header and surfacing rate-limit errors to the UI with wait time.
All tests passing; integration test for end-to-end rate-limit surface path planned in follow-up.

Verification

  • Edge function search-artist-links: call with artist name and trigger Spotify 429; verify retries and either succeeds or returns rate-limit error with wait time
  • Link Wizard UI: when rate-limit error surfaces, verify "Rate limited. Try again in Ns" message displays instead of generic error
  • Edge function with 5xx/4xx (non-429) error: verify fails immediately without retry
  • Spotify token refresh: verify also retries on 429 before failing

Generated by Claude Code

Copilot AI lite review requested due to automatic review settings August 26, 2026 16:18
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
upline Ready Ready Preview Aug 27, 2026 6:05pm

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The retry utility currently parses Retry-After but doesn’t use it to pace 429 retries, contradicting the stated behavior and risking immediate repeated rate-limit hits.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a shared retry-with-backoff utility for Supabase edge functions and uses it to handle Spotify 429 rate limits, propagating Retry-After wait time through the API response so the Link Wizard UI can display a rate-limit-specific message.

Changes:

  • Introduces fetchWithRetry (+ unit tests) for retrying 429s with backoff and returning a structured rate-limit result.
  • Updates Spotify search and Spotify token acquisition to use the retry utility and surface rate-limit wait time.
  • Plumbs rateLimitRetryAfter through edge-function types, API Zod schema, and Link Wizard error formatting.
File summaries
File Description
supabase/functions/search-artist-links/types.ts Adds optional rateLimitRetryAfter to provider outcomes/results.
supabase/functions/search-artist-links/spotify-adapter.ts Switches Spotify search to fetchWithRetry and returns rate-limit info on 429.
supabase/functions/search-artist-links/index.ts Includes rateLimitRetryAfter in the edge-function response when present.
supabase/functions/_shared/spotify-api/auth.ts Uses fetchWithRetry for token requests and surfaces rate-limit failures.
supabase/functions/_shared/retry-utils.ts Adds new retry/backoff helper and Retry-After parsing.
supabase/functions/_shared/retry-utils.test.ts Adds Deno unit tests covering retry behavior and Retry-After parsing.
src/pages/admin/festivals/LinkWizard/useProviderCandidates.ts Threads through rateLimitRetryAfter and formats a user-facing rate-limit message.
src/api/artistSearch/types.ts Extends client schema/types to accept rateLimitRetryAfter.
Review details

Suppressed comments (1)

supabase/functions/_shared/retry-utils.ts:50

  • On 429, the retry sleep uses only exponential backoff; it should consider the parsed Retry-After value so we don’t retry earlier than the server indicates (subject to maxDelayMs).
          if (attempt < maxRetries) {
            const delay = Math.min(
              initialDelayMs * Math.pow(2, attempt),
              maxDelayMs,
            );
  • Files reviewed: 9/9 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/pages/admin/festivals/LinkWizard/useProviderCandidates.ts Outdated
Comment thread supabase/functions/_shared/retry-utils.ts
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown

Deploy → stagingworkflow run
Last updated: 2026-08-27 18:06:07 UTC

  • ⏭️ DB migrations skipped (no changes)
  • Edge functions succeeded

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown

Playwright test results

passed  55 passed

Details

stats  55 tests across 16 suites
duration  1 minute, 15 seconds
commit  6ac01f5

claude added 5 commits August 27, 2026 17:59
Implement retry-with-backoff helper for handling Spotify API 429 responses:
- New fetchWithRetry utility honors Retry-After header for rate limits
- Spotify search and token refresh now retry twice with exponential backoff
- Rate-limit errors are distinguishable from other failures with wait time
- UI displays "Rate limited. Try again in Ns" message when applicable
- Includes comprehensive unit tests for retry scenarios

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2ci2Q1QgHtkMY93c2zf9C
Adds integration test covering repeated 429 responses surfacing rate-limit error through full request path. Tests three scenarios:
- 429 exhausting retries: returns distinguishable rate-limit error with Retry-After seconds
- 429 then success: recovers after one retry with backoff
- Non-429 error: fails immediately without retry

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2ci2Q1QgHtkMY93c2zf9C
…e UI from error wording

Two fixes:
1. fetchWithRetry now incorporates Retry-After header into sleep duration using max(exponentialBackoff, retryAfterMs), capped at maxDelayMs, so we honor the server's requested wait time
2. buildErrorMessage detects rate-limit via rateLimitRetryAfter field directly instead of string matching on error text, decoupling UI from backend error wording

Adds unit tests verifying:
- Retry-After value is respected and delay is at least as long as requested
- Large Retry-After values are capped at maxDelayMs to prevent hanging

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2ci2Q1QgHtkMY93c2zf9C
…use fake timers in retry tests

- getSpotifyAccessToken no longer reads .error off a RateLimitError result (TS2339)
- searchSpotify breaks out of the artist loop on a 429 instead of hammering remaining candidates with requests that will just be rate-limited again
- retry-utils.test.ts uses Deno's FakeTime instead of waiting on real delays, cutting ~3s of real time off the suite
…integration tests

getSpotifyAccessToken threw "Spotify credentials are not configured" in CI
since SPOTIFY_CLIENT_ID/SECRET aren't set for deno test. Also the module-level
token cache persisted across the file's three Deno.test cases (they share the
same imported auth.ts instance), so tests after the first skipped the mocked
token fetch and desynced their response queues.
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.

3 participants