microsoft/playwright · error · Error

No Mobile Safari tabs found at ${this._proxyBase}/json — ope

Error message

No Mobile Safari tabs found at ${this._proxyBase}/json — open Safari first.

What it means

After listing tabs from ios_webkit_debug_proxy's /json, WVBrowser._initialize requires at least one debuggable Safari tab to attach to. If the list came back empty (Safari not open, or no tab with a webSocketDebuggerUrl), connection aborts with a directive to open Safari first. This is the most common startup failure for iOS webview automation.

Source

Thrown at packages/playwright-core/src/server/webkit/webview/wvBrowser.ts:210

  _page!: WVPage;

  constructor(parent: SdkObject, proxyBase: string, headers: { [key: string]: string }, syncServer: SyncServer, directPageTransport: ConnectOverCDPTransport | undefined, options: BrowserOptions) {
    super(parent, options);
    this._proxyBase = proxyBase;
    this._headers = headers;
    this._syncServer = syncServer;
    this._directPageTransport = directPageTransport;
    this._context = new WVBrowserContext(this);
  }

  async _initialize(): Promise<void> {
    await this._context.initialize();
    if (this._directPageTransport) {
      await this._attachTab('rdp-transport', this._directPageTransport);
    } else {
      await this._syncTabs();
      if (!this._tabs.size)
        throw new Error(`No Mobile Safari tabs found at ${this._proxyBase}/json — open Safari first.`);
    }
    this._page = this._firstTab().page;
  }

  private _firstTab(): TabEntry {
    return this._tabs.values().next().value as TabEntry;
  }

  async _syncTabs(): Promise<void> {
    const tabs = await listTabs(this._proxyBase, this._headers);
    const seen = new Set<string>();
    for (const tab of tabs) {
      const pageId = pageIdFromWsUrl(tab.webSocketDebuggerUrl);
      seen.add(pageId);
      if (this._tabs.has(pageId))
        continue;
      try {
        await this._attachTab(pageId, new DeferredWebSocketTransport(tab.webSocketDebuggerUrl, this._headers));

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. On the device: open Safari and load any page, then retry connectOverCDP.
  2. Enable Settings > Safari > Advanced > Web Inspector and (if needed) remote automation.
  3. Ensure the device is unlocked and trusted, and the proxy can still see the tab via /json.

Example fix

// before
// (Safari closed on device)
await webkit.connectOverCDP('http://localhost:27753'); // No Mobile Safari tabs found

// after
// 1. On device: open Safari, navigate to a page.
// 2. Then:
await webkit.connectOverCDP('http://localhost:27753');
Defensive patterns

Strategy: validation

Validate before calling

async function ensureTab(base) {
  const tabs = await (await fetch(`${base}/json`)).json();
  const usable = tabs.filter(t => !!t.webSocketDebuggerUrl);
  if (!usable.length) throw new Error('Open Safari on the device and load a page before connecting.');
}
await ensureTab(endpointBase);

Try / catch

try { browser = await webkit.connectOverCDP(base); }
catch (e) {
  if (/No Mobile Safari tabs found/i.test(String(e.message))) {
    throw new Error('Open Safari and a page on the device, then retry.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Connecting to iOS Safari while Safari has no open tab; Safari open but the page is not debuggable (private browsing, native view); the device locked; web inspector disabled on the device.

Common situations: Forgot to open Safari on the iOS device before connecting; Web Inspector / remote automation disabled in iOS Settings; device screen locked; using a non-Safari WKWebView with no equivalent tab exposed by the proxy.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/db3889bebc81dec4. Report an issue: GitHub.