microsoft/playwright · error

WebView has been closed

Error message

WebView has been closed

What it means

Error thrown by AndroidDevice.connectToWebView() when the requested WebView socket name no longer exists in the device's list of known WebViews. This means the WebView process (the Android app embedding a WebView) has terminated or its socket was unregistered, so Playwright cannot attach a CDP session to it. To resolve, verify the WebView-enabled app is running on the device (WebView sockets appear only while the app is alive), refresh the socket name via androidDevice.webViews(), and call connectToWebView() again with a currently valid socketName.

Solutions

  1. Re-acquire the WebView: re-read androidDevice.webViews() and get a fresh handle instead of using a stale one
  2. Listen for the 'close'/'webview' close events on the device and stop issuing commands to closed WebViews
  3. Check that the Android app hosting the WebView is still running before automating it
  4. Reconnect to the device (androidDevice or adb) if the whole connection dropped, then re-launch/attach the WebView
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at packages/playwright-core/src/server/android/android.ts:331 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of microsoft/playwright@de214f440b (2026-09-01). Data as JSON: /api/errors/b71a82370c4ef520. Report an issue: GitHub.

Appendix: source

Thrown at packages/playwright-core/src/server/android/android.ts:330

    const chromeArguments = [];
    if (proxy) {
      chromeArguments.push(`--proxy-server=${proxy.server}`);
      const proxyBypassRules = [];
      if (proxy.bypass)
        proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
      if (shouldProxyLoopback(proxy.bypass))
        proxyBypassRules.push('<-loopback>');
      if (proxyBypassRules.length > 0)
        chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(';')}`);
    }
    chromeArguments.push(...args);
    return chromeArguments;
  }

  async connectToWebView(progress: Progress, socketName: string): Promise<BrowserContext> {
    const webView = this._webViews.get(socketName);
    if (!webView)
      throw new Error('WebView has been closed');
    return await this._connectToBrowser(progress, socketName, {}, /* isWebView */ true);
  }

  private async _connectToBrowser(progress: Progress, socketName: string, options: types.BrowserContextOptions = {}, isWebView?: boolean): Promise<BrowserContext> {
    const socket = await this._waitForLocalAbstract(progress, socketName);
    try {
      const androidBrowser = new AndroidBrowser(this, socket);
      await progress.race(androidBrowser._init());
      this._browserConnections.add(androidBrowser);

      const artifactsDir = await progress.race(fs.promises.mkdtemp(ARTIFACTS_FOLDER));
      const cleanupArtifactsDir = async () => {
        const errors = (await removeFolders([artifactsDir])).filter(Boolean);
        for (let i = 0; i < (errors || []).length; ++i)
          debug('pw:android')(`exception while removing ${artifactsDir}: ${errors[i]}`);
      };
      gracefullyCloseSet.add(cleanupArtifactsDir);
      socket.on('close', async () => {

View on GitHub (pinned to de214f440b)