Skip to content

feat: retry transient 5xx errors in withSpinner#242

Merged
felipefreitag merged 3 commits into
mainfrom
fix/retry-transient-5xx-43c8
May 8, 2026
Merged

feat: retry transient 5xx errors in withSpinner#242
felipefreitag merged 3 commits into
mainfrom
fix/retry-transient-5xx-43c8

Conversation

@bukinoshita

@bukinoshita bukinoshita commented Apr 9, 2026

Copy link
Copy Markdown
Member

Summary by cubic

Add opt-in retries for transient 5xx errors in withSpinner to make read paths and idempotent template writes resilient to brief outages. Addresses BU-639 by preventing failures on internal_server_error, service_unavailable, and gateway_timeout.

  • New Features

    • withSpinner now accepts { loading, retryTransient? }; always retries rate_limit_exceeded, and when retryTransient is true, also retries transient 5xx with the same backoff/retry-after logic.
    • Default retryTransient: true for runGet and runList; optional on runWrite (enabled for templates:update and templates:publish; left off for create, delete, emails:send, emails:batch; enabled for read-only emails:receiving:attachment and contact-properties:update fetch).
    • Added isTransientError utility.
  • Migration

    • Replace withSpinner('message', ...) with withSpinner({ loading: 'message' }, ...).
    • For idempotent or read-only operations, set retryTransient: true; keep it off for non-idempotent writes.
    • No changes needed where runGet/runList are used (they now retry transient errors by default).

Written for commit da1ad40. Summary will update on new commits.

@bukinoshita

Copy link
Copy Markdown
Member Author

@cursor fix conflicts

@cursor

cursor Bot commented Apr 13, 2026

Copy link
Copy Markdown

Conflicts resolved. I rebased the branch onto the latest main, resolved a merge conflict in src/commands/emails/receiving/attachment.ts (which was refactored upstream to use runGet instead of a direct withSpinner call — the runGet path already inherits retryTransient: true), and also fixed a new direct withSpinner caller in src/commands/contact-properties/update.ts that was added to main since the original PR. All 800 tests pass, typecheck and lint are clean.

Open in Web Open in Cursor 

Add opt-in retry support for transient server-side errors
(internal_server_error, service_unavailable, gateway_timeout) in the
withSpinner retry loop, alongside the existing rate_limit_exceeded
handling.

- Extract isTransientError predicate into src/lib/is-transient-error.ts
- Change withSpinner first param from string to SpinnerMessages object
  with optional retryTransient flag
- Enable retryTransient for safe read operations (runGet, runList) and
  idempotent writes (template update, template publish)
- Keep retryTransient disabled for non-idempotent commands (create,
  delete, send, batch) to avoid duplicate side effects
- Update all direct withSpinner callers to use the new object signature
- Add tests for transient retry behavior and isTransientError predicate

Co-authored-by: Bu Kinoshita <bukinoshita@users.noreply.github.com>
@cursor cursor Bot force-pushed the fix/retry-transient-5xx-43c8 branch from 1628223 to da1ad40 Compare April 13, 2026 22:29
@bukinoshita bukinoshita marked this pull request as ready for review April 13, 2026 23:46
@bukinoshita

Copy link
Copy Markdown
Member Author

@cubic-dev-ai can you review?

@cubic-dev-ai

cubic-dev-ai Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai can you review?

@bukinoshita I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot 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.

No issues found across 11 files

@felipefreitag felipefreitag 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.

Changed the API to use the new withRetry helper

@felipefreitag felipefreitag merged commit 964aa52 into main May 8, 2026
7 checks passed
@felipefreitag felipefreitag deleted the fix/retry-transient-5xx-43c8 branch May 8, 2026 20:34
felipefreitag added a commit that referenced this pull request May 11, 2026
main #242 extracted retry logic into withRetry. Apply withTimeout to the
call before passing it into withRetry, so the per-attempt timeout still
holds. Replace the brittle module-spy in the spinner test with a fake-timers
test, and cover timer-cleanup regressions in with-timeout.test.ts.
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