Repository navigation
Description
Summary
CheckoutWidget (and TransactionWidget, which shares the same hook) renders the dead-end "Token Not Supported – This token or chain is not supported by the Bridge" screen for any failure of the destination-token lookup, not only for genuinely unsupported tokens. A dropped request on mobile, a 401 from a host that is not on the client-id allowlist, a 429, a 5xx, or a non-JSON error page from the edge all end up on that screen, with no retry, no onError callback, and the verdict cached for the session.
Root cause
packages/thirdweb/src/react/web/ui/Bridge/common/token-query.ts (permalink):
const token = await getToken(params.client, tokenAddress, params.chainId).catch((err) => {
err.message.includes("not supported") ? undefined : Promise.reject(err);
});
if (!token) {
return { type: "unsupported_token" };
}The arrow function has a block body and never returns, so the .catch handler resolves to undefined for every error. Promise.reject(err) is created but discarded (it only produces an unhandled-rejection warning). token is therefore undefined on any failure and the hook reports unsupported_token.
Two knock-on effects make it worse in production:
- Because the queryFn returns instead of throwing, react-query treats the failure as a successful result: the default
retrynever runs, and withrefetchOnMount: false+ the provider'sstaleTime: 60_000the cachedunsupported_tokenresult is reused when the widget is re-mounted (for example when a user closes and reopens a checkout dialog) untilgcTimeexpires. Only a full page reload clears it. UnsupportedTokenScreendoes not callprops.onError, so integrators cannot observe or log these failures.
Additionally, if the rejection reason is not an Error (for example the "timeout" string passed to controller.abort("timeout") in getClientFetch), err.message.includes itself throws a TypeError.
Reproduction
- Render
<CheckoutWidget client={client} chain={polygon} tokenAddress="0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" amount="9" seller="0x..." />with a token that is supported by the Bridge (Polygon USDC). - In DevTools, block
bridge.thirdweb.com(Network → Request blocking), or serve the page from a host that is not on the client-id allowlist so/v1/tokensanswers401. - The widget shows "Token Not Supported" immediately;
onErroris not invoked; unblocking the host and re-mounting the widget within a few minutes still shows the same screen.
Observed on [email protected] and 5.121.1; the code is unchanged on main.
Suggested fix
const token = await getToken(params.client, tokenAddress, params.chainId).catch((err) => {
if (err instanceof Error && err.message.includes("not supported")) {
return undefined;
}
throw err;
});With this change a real "not supported" answer still renders UnsupportedTokenScreen, while every other failure propagates to tokenQuery.error, react-query retries with its default policy, and the widget falls through to its existing ErrorBanner (which already has a Retry button). It would also be worth calling props.onError from that branch so integrators can log the failure.
Happy to open a PR with this change if that helps.
Activity
linear commentedon Sep 7, 2026
BLD-570