Skip to content

Retry transport failures#8783

Closed
0xFirekeeper wants to merge 1 commit into
mainfrom
firekeeper/http3
Closed

Retry transport failures#8783
0xFirekeeper wants to merge 1 commit into
mainfrom
firekeeper/http3

Conversation

@0xFirekeeper
Copy link
Copy Markdown
Member

@0xFirekeeper 0xFirekeeper commented May 30, 2026

Add transport-layer retrying for network failures and wire it into OTP auth.

  • Introduce fetchWithTransportRetry and isTransportError to retry fetch thunks that fail before an HTTP response (TypeError), while excluding aborts/timeouts (AbortError). Retries use jittered exponential backoff (3 attempts, 300ms base).
  • Update getClientFetch to perform each attempt with its own AbortController/timeout and call fetchWithTransportRetry.
  • Extend retry utility with backoff and shouldRetry options, ensure no sleep after final attempt, and add tests for retry behavior.
  • Update fetch and OTP tests to model abort behavior and verify retry semantics.
  • Use fetchWithTransportRetry for sendOtp and verifyOtp so transient transport errors are retried without causing duplicate side effects.

PR-Codex overview

This PR introduces automatic retries for SDK and in-app wallet authentication requests during transient network failures, with a focus on avoiding duplicate side effects. It implements jittered exponential backoff for retries and updates relevant functions to utilize this new behavior.

Detailed summary

  • Added automatic retry mechanism for network failures in SDK and in-app wallet auth requests.
  • Implemented fetchWithTransportRetry to handle fetch requests with retries.
  • Updated sendOtp and verifyOtp functions to use fetchWithTransportRetry.
  • Enhanced retry function to include options for backoff and non-retryable errors.
  • Introduced utility isTransportError to identify network errors for retry logic.
  • Updated tests to cover new retry behavior and conditions for non-retryable errors.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • Bug Fixes
    • In-app wallet now automatically retries transport and authentication requests when transient network failures occur (such as timeouts or connection errors), using jittered exponential backoff for improved reliability. Retries only apply before receiving an HTTP response, preventing duplicate operations such as verification-code sending.

Review Change Stack

Add transport-layer retrying for network failures and wire it into OTP auth.

- Introduce fetchWithTransportRetry and isTransportError to retry fetch thunks that fail before an HTTP response (TypeError), while excluding aborts/timeouts (AbortError). Retries use jittered exponential backoff (3 attempts, 300ms base).
- Update getClientFetch to perform each attempt with its own AbortController/timeout and call fetchWithTransportRetry.
- Extend retry utility with backoff and shouldRetry options, ensure no sleep after final attempt, and add tests for retry behavior.
- Update fetch and OTP tests to model abort behavior and verify retry semantics.
- Use fetchWithTransportRetry for sendOtp and verifyOtp so transient transport errors are retried without causing duplicate side effects.

Includes changeset note describing the behavior.
@0xFirekeeper 0xFirekeeper requested review from a team as code owners May 30, 2026 23:08
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 30, 2026

🦋 Changeset detected

Latest commit: 0aabc2f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch
wagmi-inapp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

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

Project Deployment Actions Updated (UTC)
docs-v2 Error Error May 30, 2026 11:16pm
nebula Ready Ready Preview, Comment May 30, 2026 11:16pm
thirdweb_playground Ready Ready Preview, Comment May 30, 2026 11:16pm
thirdweb-www Ready Ready Preview, Comment May 30, 2026 11:16pm
wallet-ui Ready Ready Preview, Comment May 30, 2026 11:16pm

@github-actions github-actions Bot added packages SDK Involves changes to the thirdweb SDK labels May 30, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Walkthrough

This PR adds network-layer retry support to in-app wallet authentication requests. The retry utility is enhanced with exponential backoff and conditional retry logic, a fetch wrapper detects transport errors and retries them, timeouts are isolated per attempt, and OTP endpoints are integrated to prevent side-effect duplication during retries.

Changes

In-app wallet transport retry support

Layer / File(s) Summary
Retry utility enhancements
packages/thirdweb/src/utils/retry.ts, packages/thirdweb/src/utils/retry.test.ts
retry now accepts backoff?: boolean for jittered exponential backoff and shouldRetry?: (error) => boolean for conditional rethrow. The loop skips sleeping after the final attempt. Tests verify conditional retry behavior and confirm no extra sleep occurs after failure.
Fetch wrapper with transport-error retry
packages/thirdweb/src/utils/fetch.ts, packages/thirdweb/src/utils/fetch.test.ts
Exports isTransportError to detect TypeError (non-abort) failures and fetchWithTransportRetry to retry fetch via the enhanced retry utility. getClientFetch creates a fresh AbortController and timeout per attempt via a doFetch thunk. Test mock now rejects abort signals with DOMException("AbortError") to prevent abort-triggered retries.
OTP endpoints routed through fetch retry
packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts
sendOtp and verifyOtp POST requests are routed through fetchWithTransportRetry with comments clarifying that only pre-response network failures trigger retries, preventing duplicate OTP delivery or code consumption.
Changeset documentation
.changeset/in-app-wallet-transport-retry.md
Documents the new network-layer retry behavior using jittered exponential backoff with the boundary that retries occur only before any HTTP response is received.

Sequence Diagram

sequenceDiagram
  participant OTPClient as OTP Client
  participant fetchRetry as fetchWithTransportRetry
  participant isTransport as isTransportError
  participant retryFn as retry utility
  participant doFetch
  participant fetch as browser fetch
  OTPClient->>fetchRetry: doFetch thunk for POST
  fetchRetry->>retryFn: fn=doFetch, {backoff: true, shouldRetry: isTransportError}
  retryFn->>doFetch: attempt 1
  doFetch->>doFetch: create fresh AbortController
  doFetch->>fetch: POST with timeout signal
  fetch-->>doFetch: TypeError: Network request failed
  doFetch-->>retryFn: error
  retryFn->>isTransport: is transport error?
  isTransport-->>retryFn: true (not AbortError)
  retryFn->>retryFn: sleep exponential backoff + jitter
  retryFn->>doFetch: attempt 2
  doFetch->>doFetch: create fresh AbortController (isolated)
  doFetch->>fetch: POST with fresh timeout signal
  fetch-->>doFetch: 200 OK Response
  doFetch-->>retryFn: Response
  retryFn-->>fetchRetry: Response
  fetchRetry-->>OTPClient: Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description provides comprehensive context and implementation details, but does not follow the repository's required PR template structure. Consider restructuring the description to match the template: add '[SDK] Feature: ...' format, include 'Notes for the reviewer' section, and add 'How to test' section with test instructions.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: introducing transport failure retry logic for network requests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch firekeeper/http3

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts`:
- Around line 48-56: The POST retries for sendOtp and verifyOtp use
fetchWithTransportRetry but generate the request payload inside the retry
closure, which can cause duplicate server-side side effects; generate a stable
idempotency key (e.g. via crypto.randomUUID() or a provided client id) once
before the call and include it in the request (Idempotency-Key header or an
idempotency field on body) so the same key is sent on every retry; ensure the
idempotency key is created outside the fetchWithTransportRetry closure and added
to headers/body used by the fetch(url, { body: stringify(body), headers, method:
"POST" }) calls in sendOtp and verifyOtp so server-side dedupe can detect
retries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8eb45ec1-3dc1-4d62-9bfc-b78fe9c69a4d

📥 Commits

Reviewing files that changed from the base of the PR and between a82e633 and 0aabc2f.

📒 Files selected for processing (6)
  • .changeset/in-app-wallet-transport-retry.md
  • packages/thirdweb/src/utils/fetch.test.ts
  • packages/thirdweb/src/utils/fetch.ts
  • packages/thirdweb/src/utils/retry.test.ts
  • packages/thirdweb/src/utils/retry.ts
  • packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts

Comment on lines +48 to +56
// Only retried when the request fails before a response is received, so a
// verification code is never sent more than once.
const response = await fetchWithTransportRetry(() =>
fetch(url, {
body: stringify(body),
headers,
method: "POST",
}),
);
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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Add idempotency protection before retrying these POST auth mutations.

These retries can still duplicate side effects when a request is processed server-side but the client never receives the response. Retrying sendOtp/verifyOtp without an idempotency key + server dedupe contract risks duplicate OTP sends or unintended code consumption.

Also applies to: 119-127

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts` around lines 48 -
56, The POST retries for sendOtp and verifyOtp use fetchWithTransportRetry but
generate the request payload inside the retry closure, which can cause duplicate
server-side side effects; generate a stable idempotency key (e.g. via
crypto.randomUUID() or a provided client id) once before the call and include it
in the request (Idempotency-Key header or an idempotency field on body) so the
same key is sent on every retry; ensure the idempotency key is created outside
the fetchWithTransportRetry closure and added to headers/body used by the
fetch(url, { body: stringify(body), headers, method: "POST" }) calls in sendOtp
and verifyOtp so server-side dedupe can detect retries.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 30, 2026

size-limit report 📦

Path Size
@thirdweb-dev/nexus (esm) 105.66 KB (0%)
@thirdweb-dev/nexus (cjs) 319.47 KB (0%)

@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.80%. Comparing base (a82e633) to head (0aabc2f).

Files with missing lines Patch % Lines
...es/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts 6.66% 14 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8783      +/-   ##
==========================================
+ Coverage   52.78%   52.80%   +0.02%     
==========================================
  Files         934      934              
  Lines       62976    63017      +41     
  Branches     4137     4143       +6     
==========================================
+ Hits        33241    33278      +37     
- Misses      29635    29639       +4     
  Partials      100      100              
Flag Coverage Δ
packages 52.80% <80.00%> (+0.02%) ⬆️
Files with missing lines Coverage Δ
packages/thirdweb/src/utils/fetch.ts 85.38% <100.00%> (+2.05%) ⬆️
packages/thirdweb/src/utils/retry.ts 92.85% <100.00%> (+3.38%) ⬆️
...es/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts 5.31% <6.66%> (+0.82%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@0xFirekeeper 0xFirekeeper added the DO NOT MERGE This pull request is still in progress and is not ready to be merged. label May 30, 2026
@0xFirekeeper 0xFirekeeper deleted the firekeeper/http3 branch June 2, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE This pull request is still in progress and is not ready to be merged. packages SDK Involves changes to the thirdweb SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant