Skip to main content
This guide covers the most common issues developers encounter when integrating SocketFi, organized by category. Each issue follows a Symptom → Cause → Solution format so you can identify the problem quickly and apply the right fix. If your issue isn’t listed here, check the browser console and network tab first — most problems surface a clear error code or HTTP status.
Before troubleshooting, collect your SDK version, the exact error message or code, the environment (TESTNET vs MAINNET), and the steps to reproduce. This information makes every problem easier to diagnose.

Authentication issues

Symptom: Calling socketfi.authenticate() does nothing, or the browser shows a “popup blocked” notification.Cause: Browsers block popups that aren’t triggered by a direct user interaction (a click or keypress). If authenticate() is called inside a useEffect, a timeout, or any async chain that started without a user gesture, the browser will block it.Solution: Always call authenticate() directly from an event handler, never from lifecycle hooks or timers:
Also check that your browser settings allow popups for your domain, and that you’re testing on HTTPS or localhost.
Symptom: authenticate() throws an error with code INVALID_CLIENT_ID.Cause: The clientId passed to new SocketFi({ clientId }) doesn’t match a registered application in SocketFi.Solution: Verify that:
  • Your environment variable (VITE_SOCKETFI_CLIENT_ID) is correctly set and not empty.
  • The client ID in your SocketFi project dashboard exactly matches what your app is reading.
  • You’re using the right environment variable prefix for your bundler (VITE_ for Vite, NEXT_PUBLIC_ for Next.js).
  • You’re not accidentally using a TESTNET client ID against MAINNET or vice versa.
Symptom: The SocketFi authentication popup opens but hangs without showing a passkey prompt, or closes immediately.Cause: The user’s device or browser doesn’t support WebAuthn / passkeys, or the browser is in a state (e.g. private window on some browsers) that restricts passkey access.Solution:
  • Confirm the user is on a supported browser with WebAuthn enabled.
  • Check that the page is served over HTTPS (or localhost). Passkeys require a secure context.
  • Test in a standard (non-private) browser window. Some browsers restrict passkeys in private/incognito mode.
  • On mobile, ensure the device has at least one passkey or biometric method enrolled.
Symptom: authenticate() or requestTransaction() throws an error with code USER_CANCELLED.Cause: The user deliberately closed the authentication or approval screen.Solution: This is expected behaviour, not an application error. Handle it silently or with a neutral prompt:

Session issues

Symptom: The user is signed in, refreshes the page, and is redirected to the login screen.Cause: The session is being held in component state (useState) only, so it’s cleared on unmount.Solution: Persist the session to localStorage after a successful authenticate() call, and restore it on app startup inside AuthProvider:
See the full implementation in the React Authentication example.
Symptom: The user is logged in, navigates within the app, and is suddenly treated as unauthenticated.Cause: Usually a state management issue — either setSession(null) is called unintentionally (check logout logic), or a component re-mount resets state without checking localStorage first.Solution:
  • Audit the AuthProvider to ensure the startup useEffect only runs once (empty dependency array []).
  • Make sure your logout function is only called from explicit user action, never from an error handler or lifecycle side effect.
  • If using React StrictMode (which runs effects twice in development), confirm your session restore logic is idempotent.
Symptom: Users who were signed in are getting 401 Unauthorized from your backend, even though they haven’t logged out.Cause: The socketfiAccessToken has a limited lifetime. When it expires, verifyAuth() on your backend will reject it.Solution: Detect 401 responses in your API layer and trigger reauthentication:
Consider implementing an Axios/fetch interceptor to handle this globally rather than per-request.

Transaction issues

Symptom: requestTransaction() throws TRANSACTION_REJECTED after the approval screen is shown.Cause: The user tapped or clicked “Reject” on the hosted approval screen.Solution: Treat this as a normal user action. Show a neutral message, re-enable the submit button, and preserve the form values so the user can try again:
Symptom: requestTransaction() throws POLICY_VIOLATION.Cause: A spending policy configured on the user’s smart wallet blocked the transaction — for example, a per-transaction spending limit was exceeded.Solution:
  • Surface a clear explanation to the user: “This transaction exceeds your wallet’s spending limit.”
  • Check your application’s wallet policy configuration in the SocketFi dashboard.
  • If appropriate, suggest a smaller transaction amount.
Symptom: You called requestTransaction() but the promise never resolves or rejects. The UI is frozen in a loading state.Cause: Usually a popup blocker preventing the approval screen from opening, or a network connectivity issue preventing the SDK from communicating with SocketFi services.Solution:
  • Check the browser console for errors.
  • Ensure requestTransaction() is called from a direct user interaction (click handler), not from a timer or effect.
  • Implement a timeout in your UI — if the transaction hasn’t settled after a reasonable period, show an error and re-enable the form.
  • Check network connectivity and verify the SocketFi service status.
Symptom: requestTransaction() resolves successfully with a transactionHash, but the displayed balance still shows the old value.Cause: Cached query data from readContract() hasn’t been invalidated yet.Solution: After a confirmed transaction, invalidate the relevant React Query cache keys:

Backend / verifyAuth issues

Symptom: Every authenticated API request returns 401 Unauthorized, even with a freshly obtained token.Cause: A few common causes:
  • The Authorization header is missing or malformed.
  • The frontend is not attaching the token correctly.
  • The token has expired.
  • You’re passing the entire session object instead of just the socketfiAccessToken string.
Solution: Verify the request header format:
And verify what you’re extracting on the server:
Double-check that you’re reading session.socketfiAccessToken (the string), not the whole session object.
Symptom: verifyAuth() throws or returns valid: false with an INVALID_TOKEN reason.Cause: The token value is corrupted, was generated for a different environment (e.g. TESTNET token used against a MAINNET project), or has been tampered with.Solution:
  • Confirm the token is being read directly from session.socketfiAccessToken without modification.
  • Make sure your frontend and backend are configured for the same network (TESTNET or MAINNET).
  • Try signing out, signing back in to get a fresh token, and retrying.
Symptom: verifyAuth() returns valid: false with a TOKEN_EXPIRED reason, causing backend 401 responses for users who were previously signed in.Cause: The socketfiAccessToken has a limited lifetime. After expiry, verifyAuth() rejects it.Solution: Detect 401 responses in your API layer and redirect the user to re-authenticate:
Consider a global fetch interceptor (Axios or a custom useFetch hook) so you handle token expiry consistently across your whole app.
Symptom: verifyAuth() returns valid: false with an INVALID_SIGNATURE reason even for tokens that appear correct.Cause: The token’s cryptographic signature could not be verified. This usually means the token was issued by a different project or environment, or the token value was altered in transit or storage.Solution:
  • Confirm you’re not accidentally modifying the token string (e.g. trimming, encoding, or base64-wrapping it) before passing it to verifyAuth().
  • Ensure the token is read verbatim from session.socketfiAccessToken.
  • Confirm your frontend and backend target the same SocketFi environment (TESTNET vs MAINNET).
  • If the problem persists, sign out and back in to obtain a fresh, correctly-signed token.
Symptom: Inside a route handler, req.user is undefined even though you added the auth middleware.Cause: Either the middleware isn’t applied to that route, or it isn’t being awaited properly.Solution:
  • Confirm authMiddleware is applied before the route handler in your Express setup.
  • Make sure the middleware calls next() on success (not just sets req.user).
  • Check that authMiddleware is declared as async and that await verifyAuth(token) is awaited.
  • Ensure your TypeScript express.d.ts declaration file is included in your tsconfig.json so req.user has the right type.
Symptom: Token verification passes or fails inconsistently across test cases, even though the tokens are correct.Cause: @socketfi/server caches public keys between calls to reduce network overhead. In test environments, this cache can carry state across test runs and produce unexpected results.Solution: Call clearKeyCache() in a beforeEach or afterAll hook to reset the cache between tests:
This is only needed in test environments. In production, the cache refreshes automatically.

React Native issues

Symptom: The authentication flow opens in a browser or WebView but never returns to the app, even though the scheme appears to be configured.Cause: The URL scheme isn’t registered in the platform-level build configuration, or there’s a mismatch between the scheme in app.json and what was passed to the SDK.Solution:
  • For Expo managed workflow: verify app.json includes "scheme": "yourappscheme" and rebuild with expo prebuild.
  • For bare React Native: verify the scheme is registered in AndroidManifest.xml and Info.plist.
  • Confirm the returnTo value exactly matches the registered scheme including the :// separator.
Symptom: Users must sign in every time they launch the app on React Native / Expo.Cause: The session is being stored in memory (useState) rather than persistent secure storage. Memory state is lost when the app process ends.Solution: Use expo-secure-store (or an equivalent) instead of localStorage:

Debugging checklist

When you’re stuck, work through this list before escalating: