{"record":{"id":"ceb6e529f2c38bf0","repo":"can1357/oh-my-pi","slug":"target-targetid-is-no-longer-available-on-the-a","errorCode":null,"errorMessage":"Target ${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":1139,"sourceCode":"\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.\n\t\t\tconst page = this.#page;\n\t\t\tif (payload.mode === \"headless\" && page && !page.isClosed()) {\n\t\t\t\tawait page.close().catch(() => undefined);\n\t\t\t}\n\t\t\tthis.#transport.send({ type: \"init-failed\", error: errorPayload(error) });\n\t\t}\n\t}\n\n\tasync #findAttachedTarget(targetId: string): Promise<Target> {\n\t\tif (!this.#browser) throw new ToolError(\"Browser is not connected\");\n\t\tfor (const target of this.#browser.targets()) {\n\t\t\tif ((await targetIdForTarget(target).catch(() => \"\")) !== targetId) continue;\n\t\t\treturn target;\n\t\t}\n\t\tthrow new ToolError(`Target ${targetId} is no longer available on the attached browser`);\n\t}\n\n\t/**\n\t * Tell the omp browser relay this worker drives the adopted page, so the\n\t * relay adds it to the per-window \"omp\" tab group. Best-effort: plain CDP\n\t * backends (real Chrome, cmux) reject the relay-private method.\n\t */\n\tasync #claimRelayTarget(page: Page): Promise<void> {\n\t\tlet session: CDPSession | undefined;\n\t\ttry {\n\t\t\tsession = await page.createCDPSession();\n\t\t\t// Puppeteer's protocol map cannot express the relay-private method; the\n\t\t\t// send signature is otherwise identical.\n\t\t\tconst raw = session as unknown as { send(method: string): Promise<unknown> };\n\t\t\tawait raw.send(\"OMP.claimTarget\");\n\t\t} catch {\n\t\t\t// Not the omp relay; nothing to claim.\n\t\t} finally {","sourceCodeStart":1121,"sourceCodeEnd":1157,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L1121-L1157","documentation":"#findAttachedTarget iterates `browser.targets()` comparing each target's id to the requested targetId; when no target matches, it throws that the target is no longer available. It is the authoritative 'this tab does not exist in the attached browser' error.","triggerScenarios":"Adopting/operating on a targetId that was closed, navigated away, or never existed in the currently attached browser; stale target IDs from a previous session or after a page transitioned to a different target type.","commonSituations":"Script holds target IDs across a browser restart; user closed the tab; SPA/page navigation replaced the target; ID typo or ID copied from a different browser instance.","solutions":["Re-list `browser.targets()` and select a currently valid targetId","Reopen the target (open a new tab with the same URL) if the content is still needed","Refresh cached target IDs after navigation or reconnect events","Validate targetId against a fresh target list before each operation in long-running scripts"],"exampleFix":"// before\nawait worker.runInTab(cachedTargetId, code);\n// after\nconst live = await worker.listTargets();\nconst id = live.find(t => t.url === expectedUrl)?.id ?? (await worker.openTab(expectedUrl)).id;\nawait worker.runInTab(id, code);","handlingStrategy":"validation","validationCode":"const live = await worker.listTargets();\nif (!live.some(t => t.id === targetId)) {\n  targetId = (await worker.openTab(url)).id;\n}","typeGuard":"function hasTarget(targets: {id:string}[], id: string): targets is {id:string}[] & {find(t: {id:string}): boolean} {\n  return targets.some(t => t.id === id);\n}","tryCatchPattern":"try {\n  await worker.runInTab(targetId, code);\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes('no longer available')) {\n    const fresh = await worker.openTab(url);\n    return worker.runInTab(fresh.id, code);\n  }\n  throw err;\n}","preventionTips":["Resolve target IDs fresh from browser.targets() instead of caching them","Refresh IDs after page navigations that can swap the underlying target","Track browser 'targetdestroyed'-style events to drop dead IDs eagerly","Validate the ID against a live listing before each operation in scripts that run long"],"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"}