Context
Driving a heavy ad-platform SPA (ads.tiktok.com Ads Manager) through the omp browser relay over raw CDP (ws://127.0.0.1:9224/cdp). The relay is the human's own Chrome, so a wrong diagnosis here is expensive: on this origin, repeated page loads had previously saturated a 4-digit verification challenge and put the ad account into a cooldown of unknown duration.
The ambiguous failure
Target.createTarget {url: <ads.tiktok.com URL>, background: true} returned a plausible target id. Then:
- The tab never appeared in
http://127.0.0.1:9224/json/list. Target.attachToTarget {targetId, flatten: true}failed withCannot attach to tab <id>.
That is indistinguishable, from the agent's side, between three very different causes: (a) the relay/extension is broken, (b) the CDP recipe is wrong, (c) the origin itself is refusing the automated session. The tempting move is to retry the create, or to reload. On an origin with a verification-challenge tripwire, that is exactly the move that burns the account.
The lesson: spend one load on a benign-origin control
Before concluding anything, issue the identical CDP sequence against a throwaway origin:
Target.createTarget {url: "https://example.com/", background: true}
-> appears in /json/list
Target.attachToTarget {targetId, flatten: true}
-> session id
Runtime.evaluate "location.host"
-> "example.com"If the control passes, the relay and the recipe are both healthy and the failure is origin-specific. That single cheap call collapses three hypotheses into one and, crucially, it tells you to stop rather than retry.
In this case the control passed cleanly. Reusing that same already-attached tab and navigating it with location.assign to the ad-platform URL then produced a human-visible "Network error. Try again later." popup: the platform's generic API-refusal toast. Conclusion: the origin fingerprints the CDP-driven session and refuses it at the API layer. No verification prompt appeared, and none was interacted with. Total cost: two page loads against the sensitive origin, and a definitive answer.
Why the control matters more than it looks
Without it, the honest write-up is "the relay might be broken, or the site might be blocking us, unknown" and the next agent re-runs the same probe to find out, spending more loads on the tripwire. With it, the finding is durable and actionable: this origin is not agent-drivable via the relay, so route around the browser entirely (the platform's Marketing API) or hand the task to the human. That went straight into the platform playbook so no future session rediscovers it.
Generalizable rules
- Never let a single failed probe against a sensitive origin be your only datapoint. Add a control on an origin where failure is free.
- A returned target/handle id is not proof the object exists. Verify against the transport's own listing (
/json/list) before trusting it. Note that the omp relay does not implementTarget.getTargets, so/json/listis the only listing. - "Cannot attach" plus "absent from the tab list" is a self-closing or never-created tab, not a transient error. Do not retry-loop it.
- Attach first, then navigate. Creating a tab directly at a hostile URL gives you nothing to inspect when it vanishes. Creating it at
about:blank, attaching, enablingPage/Inspector, and only then callinglocation.assignkeeps the session and its events in your hands. - Clean up. Close probe tabs and the CDP session, then re-read
/json/listto confirm the human's tab set is back to what it was.