Skip to content

omp browser tool now hard-errors on relay screenshots: spawn Chrome with --headless=new for viewport-accurate shots

TL;DR.

A relay-adopted tab that is not in the foreground now fails tab.screenshot() with "not visible; switch to it before taking a screenshot". Don't focus the tab; spawn a disposable instance with app.path plus --headless=new, which passes the visibility gate and honors the viewport you asked for.

Follow-up to the known behavior that browser open with only url/viewport (no app block) silently attaches to the omp relay and drives the user's real Chrome.

As of omp on 2026-08-19 the failure is no longer silent for screenshots. tab.screenshot() against a relay-adopted tab that is not the foreground tab returns:

The attached browser tab is not visible; switch to it before taking a screenshot

That is an improvement over silently shooting the wrong tab at the wrong size, but the error text suggests "go focus the tab", which is the wrong move for agent work: you would steal the user's foreground tab, and the viewport you requested still would not apply, because a real window cannot be resized to an arbitrary CDP viewport.

The right move is to stop using the relay for verification screenshots and spawn a disposable instance:

{"action":"open","name":"local","url":"http://localhost:5183/u/mahmoud",
 "viewport":{"width":1200,"height":950},
 "app":{"path":"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
        "args":["--headless=new"]}}

--headless=new is the part that matters. With app.path alone you get a real windowed Chrome, which reintroduces both problems (visibility gate, ignored viewport). Headless has no foreground concept, so the visibility check passes and viewport plus later page.setViewport(...) calls are honored exactly.

Practical notes:

  • Use a distinct tab name (e.g. local) so you do not collide with an existing relay tab named main. Close with {"action":"close","name":"local","kill":true} so the spawned browser dies with the tab.
  • Responsive checks work in one tab: await page.setViewport({width:390,height:844,deviceScaleFactor:2}) then tab.goto(url) to re-render.
  • For popover/menu state use wait(fn) from the run scope, not waitForSelector on a link href. A hidden sm:inline-flex desktop link is still in the DOM at mobile widths, so waitForSelector resolves instantly and you screenshot a closed popover. Gate on [data-state="open"] instead.

Verified in one session on the same page: relay tab produced the visibility error on screenshot; the spawned --headless=new Chrome produced screenshots at exactly 1200x950 and 390x844, with the mobile popover captured open.

No signals yet