Skip to content

fix(wallets): surface transactionId and status on 5xx transaction API failures#1977

Draft
alfonso-paella wants to merge 1 commit into
mainfrom
devin/1784140405-surface-transaction-id-on-5xx
Draft

fix(wallets): surface transactionId and status on 5xx transaction API failures#1977
alfonso-paella wants to merge 1 commit into
mainfrom
devin/1784140405-surface-transaction-id-on-5xx

Conversation

@alfonso-paella

Copy link
Copy Markdown
Contributor

Description

A customer reported that their first sendTransaction on a Solana smart wallet (which triggers the on-chain AddSigner bootstrap) landed successfully on-chain, but the SDK raised a bare "API request failed: 500" to the client. An unrecoverable-looking error on an operation that actually succeeded invites a naive retry — in a trading context that means double-buy risk.

Root cause: ApiClient.makeRequest (common base) throws ApiClientError on any 5xx regardless of the operation's actual outcome, and no layer of the create/approve/poll pipeline attaches the transactionId before the error reaches the caller — even at approve/poll sites where the ID is already known locally. (PR #1966's polling rework did not change error propagation.)

This PR keeps the pipeline unchanged and adds the smallest reconciliation path: a new exported TransactionApiError extends CrossmintSDKError carrying transactionId? and status?, plus a wrapTransactionApiError(error, transactionId?) helper applied at each throw site:

// create (solana.ts, api/client.ts): id best-effort parsed from 5xx responseBody
catch (error) { throw wrapTransactionApiError(error) ?? error; }

// approve lookup, approve submission (wallet.ts), polling (operation-poller.ts): id known locally
catch (error) { throw wrapTransactionApiError(error, transactionId) ?? error; }

Behavioral change: a 5xx during create/approve/poll now throws TransactionApiError instead of a raw ApiClientError. When the transaction ID is known, the message includes (transactionId: ...) and guidance to check status before retrying; when unknown (opaque create 5xx), the message states the outcome is indeterminate. Clients can catch TransactionApiError, read error.transactionId, and reconcile (e.g. fetch transaction status) instead of blindly retrying sendTransaction.

Open product question: this PR deliberately re-throws (does not swallow) 5xx even when the transaction may have succeeded — swallowing vs. polling-through on create-5xx is a product decision. The safe minimal change here only makes the error reconcilable.

Test plan

  • New vitest cases in @crossmint/wallets-sdk:
    • create 5xx with {"id": ...} body → TransactionApiError with transactionId set
    • create 5xx with opaque body → TransactionApiError with status set, transactionId undefined, "indeterminate" message
    • poll 5xx → transactionId attached
    • approve lookup 5xx → transactionId attached
  • pnpm lint, pnpm --filter @crossmint/wallets-sdk... build && pnpm --filter @crossmint/wallets-sdk test:vitest (653 passed, 68 skipped integration)

Package updates

  • @crossmint/wallets-sdk — patch changeset added (.changeset/quiet-wombats-reconcile-transaction-5xx.md)

Link to Devin session: https://crossmint.devinenterprise.com/sessions/57c0d09399aa4688aaef9347747ff79d
Requested by: @alfonso-paella

Co-Authored-By: Alfonso <alfonso@paella.dev>
@alfonso-paella alfonso-paella self-assigned this Jul 15, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor
Original prompt from Alfonso

# Fix: Crossmint SDK raises 500 for a transaction that actually succeeded

Repo: Crossmint/crossmint-sdk (has an environment blueprint; pnpm build, pnpm lint, and pnpm --filter @crossmint/wallets-sdk... build &amp;&amp; pnpm --filter @crossmint/wallets-sdk test:vitest are the relevant commands. Changesets required for any packages/ change: pnpm change:add).

#``# Context (from a customer report — you do NOT have access to the parent session)
A customer building a Solana smart-wallet + Jupiter demo reported:

> My first sendTransaction call triggered the on-chain signer bootstrap (AddSigner). The transaction landed successfully on-chain, but the SDK raised "API request failed: 500" to the client. An error on a succeeded operation invites naive retries, which in a trading context means double-buy risk. Returning the transactionId with the error would at least let clients reconcile.
>
> Reading the SDK source, ApiClient.makeRequest throws on any 5xx regardless of the operation's actual outcome, and the transaction create/poll pipeline was reworked in early July (crossmint-sdk PR #1966).

#``# Your task

  1. Locate ApiClient.makeRequest (or its current equivalent) and the transaction create/poll pipeline (SolanaWallet.sendTransaction and the ``@crossmint/wallets-sdk create-transaction flow). Review PR `#`1966 to understand the recent rework.
  2. Characterize the exact failure: when create-transaction returns a 5xx but the transaction was created / landed, what does the SDK surface to the caller? Confirm whether any identifier (transactionId) is available at the throw site.
  3. Implement a focused fix so a 5xx does not present as an unrecoverable error that encourages a blind retry when the operation may have succeeded. At minimum, attach the transactionId (and any status) to the thrown error so clients can reconcile. Prefer the smallest change that gives callers a safe reconciliation path; do not redesign the pipeline.
  4. Add/adjust unit tests (vitest) coverin... (611 chars truncated...)

@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@changeset-bot

changeset-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fb02f0f

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

This PR includes changesets to release 9 packages
Name Type
@crossmint/wallets-sdk Patch
@crossmint/wallets-quickstart-devkit Patch
@crossmint/wallets-playground-react Patch
@crossmint/client-sdk-react-base Patch
@crossmint/client-sdk-react-native-ui Patch
@crossmint/client-sdk-react-ui Patch
@crossmint/wallets-playground-expo Patch
@crossmint/auth-ssr-nextjs-demo Patch
@crossmint/client-sdk-nextjs-starter 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

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.

1 participant