Skip to content

Browser extension relay: tab not found when multiple tabs have same URL #1998

@jonit-dev

Description

@jonit-dev

Bug Description

When using the Chrome extension relay (profile=chrome) with multiple tabs open, the snapshot and other browser actions fail with tab not found error, even though tabs action correctly lists all attached tabs.

Root Cause

Playwright's newCDPSession(page) tries to use Target.attachToBrowserTarget to create a CDP session for mapping pages to targetIds. However, Chrome's extension debugger API doesn't support this method and returns:

{"code":-32000,"message":"Not allowed"}

This causes findPageByTargetId() in pw-session.js to fail for all pages, resulting in "tab not found".

Reproduction Steps

  1. Attach multiple Chrome tabs via the browser relay extension
  2. Run browser action=tabs profile=chrome - works, shows all tabs
  3. Run browser action=snapshot profile=chrome targetId=<any-valid-id> - fails with "tab not found"

Proposed Fix

Add a fallback in findPageByTargetId() that matches pages by URL when CDP sessions fail:

async function findPageByTargetId(browser, targetId, cdpUrl) {
    const pages = await getAllPages(browser);
    // First, try the standard CDP session approach
    for (const page of pages) {
        const tid = await pageTargetId(page).catch(() => null);
        if (tid && tid === targetId)
            return page;
    }
    // If CDP sessions fail (e.g., extension relay), try URL matching
    if (cdpUrl) {
        try {
            const baseUrl = cdpUrl.replace(/\/+$/, '').replace(/^ws:/, 'http:').replace(/\/cdp$/, '');
            const response = await fetch(`${baseUrl}/json/list`);
            if (response.ok) {
                const targets = await response.json();
                const target = targets.find(t => t.id === targetId);
                if (target) {
                    // Match by URL (works for unique URLs)
                    const urlMatch = pages.filter(p => p.url() === target.url);
                    if (urlMatch.length === 1) {
                        return urlMatch[0];
                    }
                    // For multiple URL matches, use index-based matching
                    const sameUrlTargets = targets.filter(t => t.url === target.url);
                    if (sameUrlTargets.length === urlMatch.length) {
                        const idx = sameUrlTargets.findIndex(t => t.id === targetId);
                        if (idx >= 0 && idx < urlMatch.length) {
                            return urlMatch[idx];
                        }
                    }
                }
            }
        } catch { }
    }
    return null;
}

Also update call sites to pass cdpUrl:

  • getPageForTargetId()
  • closePageByTargetIdViaPlaywright()
  • focusPageByTargetIdViaPlaywright()

Environment

  • Clawdbot version: latest (npm)
  • OS: WSL2 Ubuntu
  • Chrome extension relay connected
  • Multiple tabs attached

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions