Unstable connection functionality in android

  • Describe your issue or question:

      • Which platform or framework are you using (e.g., React, Vue, Android, iOS, Unity, Node, etc.)? Vue2

      • Which Web3Auth/ Embedded Wallet SDK (SDK Name and version) are you using?@web3auth/modal to version 10.8.

      • What is not working as expected? After migration to v10.5.4, connection in mobiles, both android and iphones, was not working . After making some changes: upgrading to v 10.8 and changing logic to avoid asyncronous initialization, connection in mobile works fine if mobile has wifi connection,
        otherwise the connection fails most of the times. Before migration to version 10, connection in mobile to web3auth worked fine.

  • Code snippets: Include the portion of your code that is causing problems.

  • web3Auth.js:

  • import { WEB3AUTH_NETWORK, Web3Auth } from '@web3auth/modal';
    
    let instance = null;
    function setWeb3AuthInstance(newInstance) {
      instance = newInstance;
      return instance;
    }
    export function getWeb3AuthInstance() {
      return instance;
    }
    
    /**
     * Return a list of localStorage keys that are related to Web3Auth or its adapters.
     */
    function getWeb3AuthRelatedStorageKeys() {
      if (typeof window === 'undefined' || !window.localStorage) return [];
      const keys = [];
      for (let i = 0; i < window.localStorage.length; i += 1) {
        const key = window.localStorage.key(i);
        if (key) {
          const lower = key.toLowerCase();
          const isWeb3AuthKey = key === 'Web3Auth-state'
            || lower.startsWith('web3auth')
            || lower.startsWith('web3auth_')
            || lower.startsWith('loglevel:web3auth')
            || lower.startsWith('torus')
            || lower.startsWith('loglevel:torus')
            || lower.startsWith('loglevel:broadcast-channel')
            || lower.startsWith('loglevel:auth')
            || lower.startsWith('mm-sdk-anon-id')
            || key === 'auth_store';
          if (isWeb3AuthKey) keys.push(key);
        }
      }
      return keys;
    }
    
    /**
     * Best-effort cleanup of any cached Web3Auth state from storage.
     * This helps recover from stuck init states due to stale cachedConnector/idToken.
     */
    export function clearWeb3AuthStorage() {
      try {
        const keys = getWeb3AuthRelatedStorageKeys();
        keys.forEach((k) => window.localStorage.removeItem(k));
      } catch (_) {
        // noop
      }
      try {
        // Some adapters might use sessionStorage flags
        if (typeof window !== 'undefined' && window.sessionStorage) {
          const ssKeys = [];
          for (let i = 0; i < window.sessionStorage.length; i += 1) {
            const key = window.sessionStorage.key(i);
            if (key) {
              const lower = key.toLowerCase();
              if (lower.includes('web3auth') || lower.includes('torus')) {
                ssKeys.push(key);
              }
            }
          }
          ssKeys.forEach((k) => window.sessionStorage.removeItem(k));
        }
      } catch (_) {
        // noop
      }
    }
    
    /**
     * Detects a known stale Web3Auth cached state that can prevent init from reaching "ready".
     */
    function hasStaleAuthCache() {
      try {
        const raw = window.localStorage.getItem('Web3Auth-state');
        if (!raw) return false;
        const parsed = JSON.parse(raw);
        return !!(parsed && parsed.cachedConnector === 'auth' && parsed.idToken == null);
      } catch (_) {
        return false;
      }
    }
    
    export async function web3AuthInit() {
      const clientId = import.meta.env.VUE_APP_CLIENT_ID;
      const rawChainId = import.meta.env.VUE_APP_DEFAULT_CHAIN_ID;
      const chainId = Number(rawChainId);
    
      if (!rawChainId || Number.isNaN(chainId)) {
        throw new Error('VUE_APP_DEFAULT_CHAIN_ID must be a valid number (e.g. "30" or "31")');
      }
    
      const network = chainId === 30 ? WEB3AUTH_NETWORK.MAINNET : WEB3AUTH_NETWORK.TESTNET;
    
      const web3AuthOptions = {
        clientId,
        web3AuthNetwork: network,
        whiteLabel: {
          name: 'Tropykus',
          url: 'https://tropykus.finance',
          logoLight: 'https://web3auth.io/images/w3a-L-Favicon-1.svg',
          logoDark: 'https://web3auth.io/images/w3a-D-Favicon-1.svg',
          defaultLanguage: 'es',
          dark: false,
          theme: {
            primary: '#00D1B2',
          },
        },
      };
    
      // If we detect a stale cached connector/idToken, clear Web3Auth storage before init
      if (typeof window !== 'undefined' && hasStaleAuthCache()) {
        clearWeb3AuthStorage();
      }
    
      const web3AuthInstance = new Web3Auth(web3AuthOptions);
      await web3AuthInstance.init();
      setWeb3AuthInstance(web3AuthInstance);
      return web3AuthInstance;
    }
    
    
    
    

    IThe initialization is done in a different view than the login view so that the instance is ready when the user logs in:

    Web3AuthLOginView:

    … async created() {
    
       this.setSessionProperty({ showDialogConnect: false });
    
    const web3AuthInstance = await web3AuthInit();
    
    const interval = setInterval(() => {
    
    if (web3AuthInstance.status === 'ready' || web3AuthInstance.status === 'connecting') {
    
    this.instanceReady = true;
    
    clearInterval(interval);
    
        }
    
       }, 1000);
    
      },
    

And when user chooses the login provider method this function is executed:

  • import { WALLET_CONNECTORS } from '@web3auth/modal';
    import { getWeb3AuthInstance, clearWeb3AuthStorage } from '@/services/web3Auth';
    
    • [constants.SESSION_CONNECT_WEB3AUTH]: async (
          { commit, dispatch, state },
          { loginProvider, loginHint }
        ) => {
          commit(constants.SESSION_SET_PROPERTY, { connectingWeb3Auth: true });
          setWalletConnectionEventMP(EVENT_NAME.WCS, 'WEB3AUTH');
          commit(constants.SESSION_SET_PROPERTY, { showDialogConnect: false });
          const { chainId } = state;
          try {
          const web3AuthInstance = getWeb3AuthInstance();
          const localProvider = await web3AuthInstance.connectTo(WALLET_CONNECTORS.AUTH, {
              authConnection: loginProvider,
              loginHint: loginHint
            })
            commit(constants.SESSION_SET_PROPERTY, { connectingWeb3Auth: false });
            Vue.prototype.$web3 = Vue.web3 = new ethers.providers.Web3Provider(localProvider, 'any');
      
            Vue.prototype.$tropykus = Vue.tropykus = new Tropykus(Vue.web3, null, 900000);
            Vue.tropykus.setPriceOracle(addresses[chainId].priceOracleProxy);
      
            const tropykusAccount = await Vue.tropykus.getAccount();
      
            const tropykusComptroller = await Vue.tropykus.setComptroller(
              tropykusAccount,
              addresses[chainId].comptroller
            );
            const tropykusMkts = await tropykusComptroller.getAllMarketsInstances(
              addresses[chainId].kSAT,
              addresses[chainId].kRBTC,
              addresses[chainId].krDOC
            );
      
            const tropykus = {
              account: tropykusAccount,
              comptroller: tropykusComptroller,
              mkts: tropykusMkts,
            };
      
            const account = await Vue.web3.getSigner();
            const walletAddress = await account.getAddress();
            const clientInformation = getInfoNavigation();
            const mappedClientInformation = {
              browser: clientInformation.browser,
              browser_version: clientInformation.browser_version,
              os: clientInformation.os,
              isMobile: clientInformation.isMobile,
              os_version: clientInformation.os_version,
            };
            commit(constants.SESSION_SET_PROPERTY, { clientInformation: mappedClientInformation });
      
            commit(constants.SESSION_SET_PROPERTY, { chainId });
            commit(constants.SESSION_SET_PROPERTY, { tropykus });
            commit(constants.SESSION_SET_PROPERTY, { provider: Vue.web3 });
            commit(constants.SESSION_SET_PROPERTY, { account });
            commit(constants.SESSION_SET_PROPERTY, { walletAddress });
            commit(constants.SESSION_SET_PROPERTY, { wallet: 'WEB3AUTH' });
            dispatch(constants.SESSION_GET_USER_ID, { walletAddress, source: 'fromSession' });
            dispatch(constants.SESSION_SET_TROPK_PROVIDER);
            dispatch(constants.SESSION_GET_EXPLORER, chainId);
            setWalletConnectionEventMP(EVENT_NAME.WCC, 'WEB3AUTH');
            return localProvider;
          } catch (error) {
            commit(constants.SESSION_SET_PROPERTY, { connectingWeb3Auth: false });
            const dataError = {
              message: error?.message,
              error,
            }
            const info = (typeof mappedClientInformation !== 'undefined' && mappedClientInformation) ? mappedClientInformation : getInfoNavigation();
            postLogs({
              type: 'error',
              description: 'error session connect web3auth',
              wallet_type: 'WEB3AUTH',
              address: typeof walletAddress !== 'undefined' ? walletAddress : constants.ADDRESS_ZERO,
              data: dataError,
              client_information: info,
            });
            setWalletConnectionEventMP(EVENT_NAME.WCE, 'WEB3AUTH', dataError);
            // Cleanup any cached/stale Web3Auth storage to recover from bad states
            try { clearWeb3AuthStorage(); } catch (_) {}
          }
        },
      

Exact error and logs: If the user does not use wifi, sometimes the connectin function does not get in the catch, but never is succesfully executed, and most of the times the error cathced is this one, even though the popup never opens.

client_information: {

browser: "Chrome"

browser_version: "143.0.0.0"

isMobile: true

os: "Android"

os_version: "Android 10"

}

created_at: "2025-12-23T16:10:36.061Z"

data: {

error: {

code: 5114

message: "Wallet popup has been closed by the user. "

name: "WalletLoginError"

}

message: "Wallet popup has been closed by the user. "

}

description: "error session connect web3auth"

Hi Natalia — thanks for the detailed report, appreciate the thorough code and logs. I’ve pulled out the key details for the support team and included a few targeted troubleshooting steps you can try now.

Summary (what you reported)

  • Tone: Reporting an issue (unstable connection on mobile after migration).
  • SDK: @web3auth/modal (Web3Auth modal integration — Embedded Wallet flow)
  • SDK version: upgraded to v10.8 (issue appeared after migrating to v10.5.4; you upgraded to 10.8)
  • Platform / framework: Vue 2 (mobile browsers on Android and iPhone — issue most visible on Android mobile data)
  • Behavior: Works fine on Wi‑Fi. On mobile data (or poor connectivity) the connect flow frequently fails. Most common logged error is WalletLoginError code 5114: “Wallet popup has been closed by the user.” — but you report the popup often never opens.
  • Code: You provided web3AuthInit() (init in separate view) and a Vuex action that calls web3AuthInstance.connectTo(WALLET_CONNECTORS.AUTH, {…}). You wait for status === ‘ready’ with an interval before allowing login.
  • Already attempted: avoiding asynchronous initialization and clearing Web3Auth storage on failure.

Likely causes (based on symptoms)

  • Popup being blocked because the actual connect() / connectTo() call is not executed as a direct user gesture (click). Browsers treat actions that are performed after async/await or via store actions as non-user initiated and may block popups — that yields a “popup closed” error even if the popup never visibly opened.
  • Poor / captive network: mobile data with slow connectivity or a captive portal (carrier login, restricted network) can break the popup load and cause it to close or time out.
  • Race between init completion and connect: if init hasn’t finished or instance moved from ready → connecting unexpectedly, the popup flow may fail.

Immediate suggestions to try

  1. Ensure connectTo is invoked synchronously from the user gesture (the click handler) if possible. Avoid awaiting other async operations before calling connectTo. Example: call web3AuthInstance.connectTo(…) immediately inside the click event handler rather than via an async action that awaits unrelated work first.
  2. Use a redirect-based/mobile-friendly flow as fallback. On mobile, redirect flows (or deep-link flows) are more reliable than popups when network conditions are spotty. If Web3Auth offers a redirect/loginUsingRedirect option for the adapter you’re using, try that for mobile devices.
  3. Double-check web3AuthInstance.status === ‘ready’ right before calling connectTo. If not ready, don’t defer large async work — either block the login UI until ready or call connect synchronously when it becomes ready via a direct user interaction.
  4. Reproduce with remote debugging (Chrome DevTools remote) and capture console + network logs on the mobile device to see failed requests, CORS, or timeouts.
  5. Try an incognito session / clear storage first and test on mobile data to confirm it isn’t stale-storage related (you already have clearWeb3AuthStorage() — try forcing the clear before init in a test run).
  6. If available, upgrade to the latest patch of @web3auth/modal (if anything newer than 10.8 exists) or check the library’s changelog for popup/timeout fixes.

What I need from you (to pass to support or to continue troubleshooting)

  • Confirm whether the connectTo call is executed directly inside the click handler (synchronous) or always runs through async store actions. If it’s not called synchronously, try making a quick test to call it directly in the click handler.
  • Browser console logs (mobile remote debugging) captured during a failed attempt — especially any errors or network failures. Network HAR / screenshot of the DevTools Network tab would be ideal.
  • A short screen recording or screenshot showing whether any popup appears or if there’s a visible redirect attempt.
  • Confirm whether the problem reproduces in mobile incognito mode (no extensions/cached state) and whether captive portal/login screens are present on the mobile carrier network.

If you want, I can package this into a ticket-friendly summary for the support team (including the code you pasted) — I already have the main pieces, but console HARs/screenshots will speed resolution. Which would you like me to prepare next?

Note: This response was generated by an AI bot and may require further verification. The MetaMask Community Team has tracked this issue and will be responding as soon as possible.

Additionally, we would recommend you to join our biweekly Office Hours to get this resolved even faster. MetaMask Developer Office Hours

Please send this to the suppor team.

Hi @Natalia_Munoz Are you still having this issue?