can1357/oh-my-pi · error · ToolError
Target ${payload.targetId} is no longer available on the att
Error message
Target ${payload.targetId} is no longer available on the attached browser What it means
Thrown when an attached browser target exists but Puppeteer cannot produce a Page for it (`target.page()` returned null). This happens when the target dies or becomes non-page-backed between finding it via #findAttachedTarget and materializing its page, or when the target is a background/service-worker type with no page.
Source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:1106
if (payload.mode === "headless") {
// Create the target directly so its id is reportable before
// Puppeteer waits for target/page initialization. If that wait
// wedges, the supervisor can still close the created target.
this.#page = await createTrackedHeadlessPage(this.#browser, targetId => {
this.#transport.send({ type: "page-created", targetId });
});
this.#observeDialogs();
await applyStealthPatches(this.#browser, this.#page, { browserSession: null, override: null });
if (payload.emulateViewport !== false) await applyViewport(this.#page, payload.viewport);
if (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);
} else {
const target = await this.#findAttachedTarget(payload.targetId);
// Post-timeout recycle: unblock the target BEFORE adopting the page — an open
// modal dialog or hung navigation can stall `target.page()` / ready info, and a
// stalled init used to time out and force-kill the tab.
if (payload.recover) await this.#recoverAttachedTarget(target);
const page = await target.page();
if (!page) throw new ToolError(`Target ${payload.targetId} is no longer available on the attached browser`);
this.#page = page;
await this.#claimRelayTarget(page);
this.#observeDialogs();
if (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);
}
if (payload.url) {
await this.#page.goto(payload.url, {
// Default to "load" because dev servers with HMR/WS never reach networkidle.
waitUntil: payload.waitUntil ?? "load",
timeout: payload.timeoutMs,
});
}
this.#targetId = await targetIdForPage(this.#page);
this.#transport.send({ type: "ready", info: await this.#currentReadyInfo() });
} catch (error) {
// A failed headless init leaves the worker's page orphaned in the shared
// browser (the supervisor retries with a fresh worker), so close it before
// reporting. Attach mode adopts an existing target — never close it.View on GitHub (pinned to 9690622007)
Solutions
- Re-list attached targets and pick a fresh, still-open targetId instead of the stale one
- Reconnect or reopen the tab via the browser tool before retrying the operation
- Catch this ToolError and fall back to opening a new tab with the intended URL
- Guard long workflows by re-validating the target is still attached before each phase
Example fix
// before
const target = await worker.adoptTarget(staleTargetId);
// after
const targets = await worker.listTargets();
if (!targets.some(t => t.id === staleTargetId)) {
await worker.openTab(url); // reopen instead of adopting a dead target
} Defensive patterns
Strategy: try-catch
Validate before calling
const targets = await worker.listTargets();
if (!targets.some(t => t.id === targetId)) throw new Error(`target ${targetId} not attached`); Type guard
function isTargetLive(targets: {id:string}[], id: string): boolean {
return targets.some(t => t.id === id);
} Try / catch
try {
await worker.adoptTarget(targetId);
} catch (err) {
if (err instanceof ToolError && /no longer available/.test(err.message)) {
await worker.openTab(fallbackUrl);
} else throw err;
} Prevention
- Refresh target listings before each phase of long workflows
- Never cache target IDs across navigations or reconnects
- Handle browser disconnect events by invalidating all known target IDs
- Fall back to opening a new tab rather than assuming a cached target is alive
When it happens
Trigger: Calling the browser tab adopt/open path with payload.targetId whose target was closed by the browser (tab closed, crash, navigation to a non-page target) between lookup and `target.page()`.
Common situations: Automating a tab the user manually closed; browser tab crashed (Aw, Snap) under memory pressure; target ID cached/stale from a previous listing; target transitioned to a type without a page.
Related errors
- Target ${targetId} is no longer available on the attached br
- Connected to ${cdpUrl} but puppeteer.connect failed: ${(err
- Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !==
- No page targets available on the attached browser
- browser app.cdp_url must be the HTTP CDP discovery endpoint
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7f5604784c62109e.
Report an issue: GitHub.