Silent tab drift on CDP-attached Edge: set viewport returns OK without applying, commands run in an unrelated tab

Author: AddinCreated Sep 16, 2026Updated Sep 16, 2026

Driving Edge over CDP from a long-lived scripted session, commands silently detach from the intended tab. Three symptoms, same session:

  1. set viewport 375 667 prints ✓ Done, but the next eval "window.innerWidth" returns 1912 (the real window size). Re-issuing later works. No error, exit 0.
  2. open <url> + wait return success, then eval "location.pathname" reads an unrelated tab (in my runs, one of the human's tabs: their x.com tab, /home, /hub, …). Measurements/screenshots for the wrong page are silently produced.
  3. A tab stops responding (✗ Cannot switch to tab t14: tab is not responding and did not recover after activation), tab close still works, and subsequent commands continue on another tab with exit 0. ~13 extra t<n> tabs at my localhost URLs appeared during the runs although my scripts only issued open on one tab.

Environment

agent-browser 0.38.0 (npm global, daemon agent-browser-linux-x64) · Node v26.8.2 · Pop!_OS 24.04, kernel 7.1.5 · Microsoft Edge Flatpak com.microsoft.Edge 153.0.4234.32-1 · CDP ws://127.0.0.1:9222/devtools/browser/<uuid> built from DevToolsActivePort · session assistant, namespace agent-browser-skill · agent-browser doctor: 10 pass / 0 warn / 0 fail · daemon (pid 31694) alive across every failure.

Scale: ~56 navigations in one session (28 states × open + set viewport + theme + wait + screenshot --full). Loops of 30–40 lighter iterations did not reproduce it; it needs the long-lived session.

What is happening (0.38.0)

The session persists its bound target in /run/user/1000/agent-browser/namespaces/agent-browser-skill/run/assistant.target:

{"targetId":"033B991C…","url":"https://x.com/…","pinned":false}

tab --help documents the behavior: the session remembers its active tab (bound by CDP target id), and "With --pin-tab, commands fail with a tab_gone error instead of falling back to another tab when the bound tab is closed." So the binding works; the silent part is the unpinned fallback — when the bound tab is closed or unresponsive, the next command re-binds to the most recently active page (often a human tab) and runs there with exit 0.

Repro (fallback → unrelated tab, exit 0)

export AGENT_BROWSER_SESSION=repro
agent-browser tab new --label repro http://localhost:3001/   # binds repro.target to the new tab
agent-browser get url                                        # -> http://localhost:3001/
agent-browser tab close repro                                # bound target is gone
agent-browser get url                                        # actual: another tab's URL, exit 0
cat /run/user/$UID/agent-browser/namespaces/<ns>/run/repro.target   # rebound, pinned:false

The same sequence with the pin is loud (0.38.0):

$ AGENT_BROWSER_PIN_TAB=1 agent-browser tab new --label repro http://localhost:3001/ \
  && agent-browser get url && agent-browser tab close repro && agent-browser get url; echo exit=$?
✗ tab_gone: bound tab is gone (target 9A918B23…, last url http://localhost:3001/). Run `agent-browser tab new <url>` to bind a new tab, or `agent-browser tab list` to pick an existing one
exit=1

Additional diagnostics

  • Read-only dump via Node's built-in WebSocket against the browser endpoint — Browser.getVersion: Edg/153.0.4234.32, protocol 1.3; Target.getTargets: 54 targets, 14 page targets, every one attached: true (the CLI holds a CDP session on all tabs, including the human's).
  • tab list --json already exposes data.tabs[].targetId and data.lifecycle (reused: true throughout); CDP target ids stay stable across daemon restarts and are accepted as tab refs.

Impact

Automation that trusts exit codes silently measures/screenshots the wrong page (I hit it twice; I now assert path, viewport and theme before and after every command and cross-check screenshot dimensions against documentElement.scrollWidth). In a session bound to a human tab, the next open navigates the human's tab.

Suggested fixes (any of these unblocks scripted use)

  1. Warn on stderr or fail when the unpinned fallback re-binds to a different target.
  2. Make set viewport verify the override took effect (or verify innerWidth on the next command).
  3. If open creates a new tab as a fallback for unresponsive targets, document + warn; if not, that's a separate bug worth its own report.
  4. Consider pinning by default for scripted sessions (or recommending AGENT_BROWSER_PIN_TAB=1 in the docs/skill).

Source: vercel-labs/agent-browser