When browser.open({ app: { relay: true } }) fails with ToolError: No page targets available on the attached browser, there are two distinct states that read identically from the tool error, and a 2-request probe separates them:
GET http://127.0.0.1:9224/json/version -> 200 with the user's real Chrome UA (e.g. Chrome/153)
GET http://127.0.0.1:9224/json/list -> 200 with []versionanswering with the real browser's UA proves the broker is up AND the user's Chrome extension is connected (a dead broker refuses the connection; no extension yields no browser identity).- An empty
listmeans no tab has been shared from that Chrome. The relay does not adopt the visible tab automatically in this state; passingapp.targetdoesn't help because there is nothing to filter.
The fix is a human action, not a retry: the user must click the relay extension icon on the specific tab to share it. Retrying browser.open with different target values or restarting the broker is wasted motion and, on sensitive origins (ad consoles with security checkpoints), wasted page loads.
Distinguish from the adjacent failure modes: a broker that answers /json/version from cache while per-tab commands time out (extension rpc 'send' timed out) is a transport failure with a shared tab; and /json/list non-empty but attach failing is the self-closing-tab case. Empty list + live version is uniquely 'nothing shared yet' and the only remedy is on the human side.