Skip to content

fix(card): raise apply-for-card fetch timeout to 60s#2306

Merged
Hugo0 merged 1 commit into
mainfrom
hotfix/rain-apply-timeout
Jun 29, 2026
Merged

fix(card): raise apply-for-card fetch timeout to 60s#2306
Hugo0 merged 1 commit into
mainfrom
hotfix/rain-apply-timeout

Conversation

@jjramirezn

Copy link
Copy Markdown
Contributor

Why

The first-time Rain card application path (POST /rain/cards) is structurally 7–13s:

  • 7 sequential Sumsub calls (getCollectedDocTypesgetApplicantmoveToLevel ×2 → checkRainReadinessgenerateShareToken)
  • a deliberate setTimeout(2_500) readiness sleep (apply.ts:585) waiting for Sumsub's async review to flip GREEN
  • rainService.createApplication (Rain API)
  • an optional inline issueCard when Rain approves synchronously

The global 10s fetchWithSentry default (src/utils/sentry.utils.ts) aborts that tail client-side while the backend keeps running to completion. The user sees a false card application failed error on a card application that was actually submitted — and a retry can resubmit.

Impact (PostHog, prod)

  • rain/cards … timed out after 10000ms exception fires ~280×/week across ~40 distinct users.
  • Of 120 users who hit it in the last 30d, only 28 reached card_apply_succeeded.

Change

Override timeoutMs: 60_000 on the applyForCard call only. This is a browser→api.peanut.me fetch, so the "10s because Vercel's function limit is 15s" rationale that caps the global default does not apply here. rainRequest already threads opts.timeoutMs; nothing else changes.

Not in scope (follow-up)

This is a band-aid. The durable fix is backend: parallelize the independent Sumsub calls and/or make issuance async (return processing, finish via the card_webhook_user_updated path) so users aren't blocked on a 13s spinner. Also worth confirming createApplication/issueCard idempotency given the retry-after-timeout pattern.

Risk

Low — single per-call timeout override, no behavior change on the happy path. Worst case a genuinely hung request now holds the spinner up to 60s instead of 10s before surfacing the same error.

The first-time Rain card application path is structurally 7-13s (7 sequential
Sumsub calls + a 2.5s readiness sleep + Rain createApplication + optional inline
issueCard). The global 10s fetchWithSentry default aborts that tail client-side
while the backend keeps running to completion, so users see a false 'application
failed' error on a card that was actually submitted.

PostHog: the rain/cards timeout exception fires ~280x/week across ~40 distinct
users; of 120 users who hit it in 30d only 28 reached card_apply_succeeded.

Override timeoutMs for this one call only (browser-side fetch, so the 15s Vercel
function ceiling that caps the global default does not apply here).
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
peanut-wallet Building Building Preview, Comment Jun 29, 2026 2:12pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 330be809-9d0d-4f6b-bae3-b6d36fb57c63

📥 Commits

Reviewing files that changed from the base of the PR and between 1bd95df and 7cd807a.

📒 Files selected for processing (1)
  • src/services/rain.ts

Walkthrough

In rainApi.applyForCard, the /rain/cards POST request is given an explicit timeoutMs: 60_000 instead of relying on the default timeout. Inline comments are added explaining the first-time-application flow involves sequential Sumsub calls and Rain createApplication.

Changes

Rain card application timeout

Layer / File(s) Summary
Custom timeout for applyForCard
src/services/rain.ts
Sets timeoutMs: 60_000 on the /rain/cards POST request and adds comments documenting the multi-step first-time flow that can exceed the default timeout.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: increasing the apply-for-card fetch timeout to 60 seconds.
Description check ✅ Passed The description is directly related to the timeout change and explains the problem, scope, and risk.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@github-actions

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5843.65 → 5843.8 (+0.15)
Findings: 0 net (+1 new, -1 resolved)

🆕 New findings (1)

  • critical complexity — src/services/rain.ts — CC 54, MI 60.61, SLOC 237

✅ Resolved (1)

  • src/services/rain.ts — CC 54, MI 60.66, SLOC 236

@github-actions

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1605 ran, 0 failed, 0 skipped, 24.6s

📊 Coverage (unit)

metric %
statements 54.5%
branches 37.0%
functions 42.2%
lines 54.4%
⏱ 10 slowest test cases
time test
3.6s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
0.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.3s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in updateUserById
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/services/__tests__/resolveClaimLink.test.ts › restores the pristine password after a redirect mangles the fragment
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle too long for US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid ETH address with surrounding spaces
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid US account with spaces
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid ETH address in lowercase
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@Hugo0 Hugo0 merged commit 2ec8b5d into main Jun 29, 2026
19 of 23 checks passed
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.

2 participants