{"record":{"id":"7f5604784c62109e","repo":"can1357/oh-my-pi","slug":"target-payload-targetid-is-no-longer-available","errorCode":null,"errorMessage":"Target ${payload.targetId} is no longer available on the attached browser","messagePattern":"Target (.+?) is no longer available on the attached browser","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":1106,"sourceCode":"\t\t\tif (payload.mode === \"headless\") {\n\t\t\t\t// Create the target directly so its id is reportable before\n\t\t\t\t// Puppeteer waits for target/page initialization. If that wait\n\t\t\t\t// wedges, the supervisor can still close the created target.\n\t\t\t\tthis.#page = await createTrackedHeadlessPage(this.#browser, targetId => {\n\t\t\t\t\tthis.#transport.send({ type: \"page-created\", targetId });\n\t\t\t\t});\n\t\t\t\tthis.#observeDialogs();\n\t\t\t\tawait applyStealthPatches(this.#browser, this.#page, { browserSession: null, override: null });\n\t\t\t\tif (payload.emulateViewport !== false) await applyViewport(this.#page, payload.viewport);\n\t\t\t\tif (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);\n\t\t\t} else {\n\t\t\t\tconst target = await this.#findAttachedTarget(payload.targetId);\n\t\t\t\t// Post-timeout recycle: unblock the target BEFORE adopting the page — an open\n\t\t\t\t// modal dialog or hung navigation can stall `target.page()` / ready info, and a\n\t\t\t\t// stalled init used to time out and force-kill the tab.\n\t\t\t\tif (payload.recover) await this.#recoverAttachedTarget(target);\n\t\t\t\tconst page = await target.page();\n\t\t\t\tif (!page) throw new ToolError(`Target ${payload.targetId} is no longer available on the attached browser`);\n\t\t\t\tthis.#page = page;\n\t\t\t\tawait this.#claimRelayTarget(page);\n\t\t\t\tthis.#observeDialogs();\n\t\t\t\tif (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);\n\t\t\t}\n\t\t\tif (payload.url) {\n\t\t\t\tawait this.#page.goto(payload.url, {\n\t\t\t\t\t// Default to \"load\" because dev servers with HMR/WS never reach networkidle.\n\t\t\t\t\twaitUntil: payload.waitUntil ?? \"load\",\n\t\t\t\t\ttimeout: payload.timeoutMs,\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.#targetId = await targetIdForPage(this.#page);\n\t\t\tthis.#transport.send({ type: \"ready\", info: await this.#currentReadyInfo() });\n\t\t} catch (error) {\n\t\t\t// A failed headless init leaves the worker's page orphaned in the shared\n\t\t\t// browser (the supervisor retries with a fresh worker), so close it before\n\t\t\t// reporting. Attach mode adopts an existing target — never close it.","sourceCodeStart":1088,"sourceCodeEnd":1124,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L1088-L1124","documentation":"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.","triggerScenarios":"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()`.","commonSituations":"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.","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"],"exampleFix":"// before\nconst target = await worker.adoptTarget(staleTargetId);\n// after\nconst targets = await worker.listTargets();\nif (!targets.some(t => t.id === staleTargetId)) {\n  await worker.openTab(url); // reopen instead of adopting a dead target\n}","handlingStrategy":"try-catch","validationCode":"const targets = await worker.listTargets();\nif (!targets.some(t => t.id === targetId)) throw new Error(`target ${targetId} not attached`);","typeGuard":"function isTargetLive(targets: {id:string}[], id: string): boolean {\n  return targets.some(t => t.id === id);\n}","tryCatchPattern":"try {\n  await worker.adoptTarget(targetId);\n} catch (err) {\n  if (err instanceof ToolError && /no longer available/.test(err.message)) {\n    await worker.openTab(fallbackUrl);\n  } else throw err;\n}","preventionTips":["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"],"tags":["browser","puppeteer","cdp","target-lifecycle"],"backgroundTag":"browser-target-unavailable","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}