SDK domain not authorized
Symptom: The Mesh SDK loads but immediately shows an authorization error, or the connection fails silently. May surface as a CORS error in the browser console. Root cause: Mesh validates that the SDK is loaded from an approved domain. Any domain not on your allowlist — includinglocalhost — is rejected. This most commonly affects developers testing locally or on a staging URL they forgot to add.
Fix: In the Mesh dashboard, go to Account > API keys > Access and add every domain where your app runs:
localhost:3000(or your local port)- Any staging or preview domains
- Your production domain(s)
SDK iframe blocked by Content Security Policy
Symptom: The Mesh Link modal never appears — the area where Link should render shows as a blank grey box. The browser console shows a CSP error such as:Refused to frame 'https://web.meshconnect.com/' because an ancestor violates the following Content Security Policy directive: "frame-src 'self'"

*.meshconnect.com to your CSP frame-src directive:
Ad-blocker preventing OAuth connection
Symptom: After selecting an OAuth-based integration (eg. Coinbase, Gemini), the user gets stuck on a loading spinner and never completes authentication. The Link UI may appear initially but the OAuth flow never resolves. Root cause: Ad-blocking software can interfere with Link’s ability to use browser storage and complete OAuth redirect flows. This includes browser extensions (uBlock Origin, AdBlock Plus, etc.) and browsers with built-in ad-blocking — most commonly Brave. Fix: The user should disable their ad-blocker for your domain, or switch to a standard browser without built-in ad-blocking. Ad-blocking software is not officially supported with Mesh Link.If this surfaces frequently in support, consider adding a prompt in your UI that detects ad-blocker presence and suggests disabling it before launching Link.
Link Token expired or already used
Symptom:openLink() throws an error or the user immediately sees an error screen when Link opens. The error typically references an invalid or expired token.
Root cause: Link Tokens are short-lived (10 minutes) and single-use. A token fetched at page load may expire before the user clicks the connect button. A token from a previous session can never be reused.
Fix:
- Fetch a fresh Link Token immediately before calling
openLink()— on the button click, not on page load. - After each session (successful or failed), discard the token. Never cache or reuse tokens.
Network or symbol not supported / empty catalog
Symptom: Link opens but the integration catalog is empty, or Link returns an error on thetoAddresses fields.
Root cause (most common): An ERC-20 contract address (eg. 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) was passed as the symbol field instead of the token ticker (eg. USDC). Mesh expects the token symbol, not the contract address.
Other causes:
- A
networkId/symbolcombination that doesn’t exist in Mesh’s catalog. - A misspelled token symbol.
symbol. Key network IDs for reference:
| Network | networkId |
|---|---|
| Ethereum | e3c7fdd8-b1fc-4e51-85ae-bb276e075611 |
| Solana | 0291810a-5947-424d-9a59-e88bb33e999d |
| Base | aa883b03-120d-477c-a588-37c2afd3ca71 |
networkId/symbol pairs in transferOptions.toAddresses — eg. USDC on Ethereum, Solana, and Base.
No eligible assets after account connection
Symptom: The user connects their exchange account but sees a “No eligible assets” screen. ThetransferNoEligibleAssets SDK event fires.
Root cause: The user’s connected account doesn’t hold any tokens matching the networkId/symbol pairs in your toAddresses array. This is expected behavior when there’s a genuine mismatch — not a bug — but it needs to be handled gracefully.
Common causes:
- The user holds ETH, but you only accept USDC.
- The user holds USDC on Ethereum, but
toAddressesonly includes USDC on Solana. - The user genuinely has a zero balance.
- In sandbox: accidentally using the
Mesh2test account, which has an empty portfolio.
- Broaden your
toAddressesarray to include more asset/network pairs. - Listen for
transferNoEligibleAssetsand use itsarrayOfTokensHeldpayload to show a helpful message in your UX (eg. “You don’t have USDC in that account. Try a different account or deposit manually.”). - In sandbox: use the
Meshtest account (full portfolio) for general testing. UseMesh2specifically to test your empty-state UI.
Webhook signature validation fails
Symptom: Your webhook handler rejects every incoming event with a signature mismatch, even though your webhook secret looks correct. Root cause: The HMAC-SHA256 signature in Mesh’sX-Mesh-Signature-256 header is computed over the raw request body bytes — exactly as received off the wire. The most common mistake is JSON re-serializing the body before computing the HMAC. Parsing the body and then re-serializing it (eg. calling JSON.stringify(JSON.parse(body))) can silently change key ordering, whitespace, or number formatting — producing a different byte sequence and therefore a wrong HMAC.
Fix: Capture the raw body bytes before any parsing, compute the HMAC over those raw bytes, then parse the JSON afterward.
Transfer stays pending or finalizes as failed
Symptom: A transfer is initiated but the succeeded webhook never arrives, or arrives as failed.
Common causes and fixes:
- Exchange-side delay: CEX transfers can take minutes to hours to post on-chain. The
pendingwebhook fires when the transfer is initiated at the exchange;succeededfires once it confirms on-chain. For Coinbase and Binance, this can occasionally take up to 24 hours. This is normal — build your flow to wait forsucceededbefore crediting the user. - Amount below exchange minimum: Most exchanges enforce a minimum withdrawal. If the amount is too small, the exchange rejects it. Use
minAmount/minAmountInFiatin yourtoAddressesto surface minimums before the user initiates. - Incorrect destination address format: Some exchange/network combinations enforce strict address validation. Verify that destination addresses are valid for the specified network.
- User 2FA timeout: If the user took too long to approve the MFA step, the exchange session may have expired and the transfer was never sent.
- Sandbox limitation: In sandbox, the
pendingwebhook is not guaranteed — transfers may skip directly tosucceededorfailed. Don’t build logic that depends on receivingpendingduring testing.
pending in production, contact your Mesh representative with the TransferId from the webhook payload.
OAuth integrations show blank screen or raw code in mobile WebView
Symptom: When launching an OAuth-based integration (eg. Uphold, Paribu) from inside a native app’s in-app browser, the user sees a blank white screen or a page of raw code instead of the provider’s login screen. The OAuth flow never completes. Root cause: Mesh Link’s OAuth flows open the provider’s authorization URL in a new window viawindow.open(). Native in-app browsers — iOS WKWebView, Android WebView, and React Native / Flutter equivalents — block or silently drop window.open() calls by default unless the host app explicitly implements a popup handler. The two symptoms reflect slightly different failure modes:
- Blank screen: The WebView opens an empty window but never navigates to the OAuth URL — the popup is created but the navigation is discarded.
- Raw code screen: The OAuth callback with an authorization code lands in the wrong context and renders as a plain page rather than being intercepted by the SDK.
window.open() at the native layer:
- iOS (
WKWebView): Implement theWKUIDelegatemethodwebView(_:createWebViewWith:for:windowFeatures:)to catch popup navigations and load them in a newWKWebVieworSFSafariViewController. - Android (
WebView): OverrideWebChromeClient.onCreateWindow()to handle new-window requests. - React Native: Use
react-native-inappbrowser-rebornor open OAuth URLs via the device browser (Linking.openURL) rather than inside a WebView.
This is a common limitation of WebView environments. If your product is a mobile app, test OAuth integrations inside the actual in-app browser early — behavior can differ significantly from a standard desktop or mobile browser.
Duplicate webhook deliveries
Symptom: Your webhook handler receives the same event multiple times, causing duplicate processing (eg. crediting a user twice). Root cause: Mesh delivers webhooks with at-least-once semantics. If your server returns a non-2xx response or takes too long to respond, Mesh retries the delivery. Fix: Use theEventId field as an idempotency key. Store every processed EventId and skip any event you’ve already handled. Return 200 OK quickly (before heavy processing), then handle the event asynchronously.
EventId vs Id: The Id field changes on each delivery attempt. The EventId stays constant for the same logical event across all retries. Always deduplicate on EventId.AI coding reference (llms.txt)
AI coding reference (llms.txt)
AI coding reference — a compact summary of this page’s APIs, parameters, and patterns for use by AI coding assistants (following the llms.txt standard). Human readers can safely ignore this.llms.txt — Common errorsTroubleshooting guide for the most common Mesh integration errors.SDK domain not authorized (CORS error / auth error on load): Dashboard > Account > API keys > Access → add all domains including
localhost:3000.SDK iframe blocked by CSP (blank grey box): Add *.meshconnect.com to frame-src CSP directive.Ad-blocker interfering with OAuth (spinner, OAuth never resolves): User must disable ad-blocker or use standard browser. Brave and uBlock Origin are common culprits.Link Token expired or already used (error on openLink()): Fetch new token on button click (not page load). Tokens are 10 min, single-use. Never cache or reuse.Empty catalog / symbol not supported: Use ticker symbol (e.g. USDC), not ERC-20 contract address. Include multiple networkId/symbol pairs.Webhook signature validation fails: Header is X-Mesh-Signature-256. Compute HMAC-SHA256 over raw request body bytes before any parsing. Use express.raw() not express.json() in Node.js.Duplicate webhook deliveries: Use EventId as idempotency key. Return 200 immediately, process async.OAuth blank screen / raw code in mobile WebView: Implement window.open() handler at native layer: iOS → WKUIDelegate, Android → WebChromeClient.onCreateWindow(). Or Linking.openURL in React Native.Transfer stuck pending / finalizes as failed: CEX delay is normal (up to 24h); check minAmount in toAddresses; verify destination address format; sandbox pending webhook not guaranteed.