can1357/oh-my-pi · error · ToolError

Target id unavailable from CDP target info

Error message

Target id unavailable from CDP target info

What it means

Same failure family as the tab-supervisor variant: when resolving a Target's id, the fast path (`privateTargetId`) misses, so a temporary CDP session issues `Target.getTargetInfo`. If the response lacks `targetInfo.targetId`, this ToolError is thrown and the session is detached in `finally`.

Source

Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:697

	const err = new Ctor(payload.message);
	if (payload.name) err.name = payload.name;
	if (payload.stack) err.stack = payload.stack;
	return err;
}

function privateTargetId(target: Target): string | undefined {
	const raw = target as unknown as { _targetId?: unknown };
	return typeof raw._targetId === "string" ? raw._targetId : undefined;
}

async function targetIdForTarget(target: Target): Promise<string> {
	const fastTargetId = privateTargetId(target);
	if (fastTargetId) return fastTargetId;
	const session = await target.createCDPSession();
	try {
		const info = (await session.send("Target.getTargetInfo")) as { targetInfo?: { targetId?: string } };
		if (info.targetInfo?.targetId) return info.targetInfo.targetId;
		throw new ToolError("Target id unavailable from CDP target info");
	} finally {
		await session.detach().catch(() => undefined);
	}
}

async function targetIdForPage(page: Page): Promise<string> {
	return await targetIdForTarget(page.target());
}

async function createTrackedHeadlessPage(browser: Browser, reportTarget: (targetId: string) => void): Promise<Page> {
	const session = await browser.target().createCDPSession();
	let targetId: string;
	try {
		({ targetId } = await session.send("Target.createTarget", { url: "about:blank" }));
		reportTarget(targetId);
	} finally {
		await session.detach().catch(() => undefined);
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Skip non-page targets before resolving ids (`target.type() === "page"` check).
  2. Treat the error as 'target is gone': drop the tab from tracking and re-enumerate `browser.targets()`.
  3. Upgrade/pin puppeteer so the private `_targetId` fast path is available again.
  4. Retry the lookup after a short delay if the target is expected to settle.

Example fix

// before
const id = targetIdForPage(page); // throws if CDP info is empty
// after
try {
  const id = targetIdForPage(page);
} catch {
  await supervisor.forgetTab(page); // target vanished; re-list tabs instead of failing
}
Defensive patterns

Strategy: fallback

Validate before calling

const candidates = browser.targets().filter(t => t.type() === "page");
if (candidates.length === 0) throw new Error("no page targets attached");

Try / catch

try {
  const id = await targetIdForPage(page);
} catch {
  const id = (await browser.pages()).find(p => p === page) ? undefined : null;
  // fall back to re-listing pages and matching by URL
}

Prevention

When it happens

Trigger: `targetIdForTarget` / `targetIdForPage` / `#findAttachedTarget` invoked on a target that is detaching or closing — the CDP round-trip succeeds structurally but carries no target id.

Common situations: Tab closed concurrently with bookkeeping; service-worker or extension-background targets enumerated by mistake; puppeteer upgrade removing the `_targetId` fast path so every lookup hits the fragile CDP fallback.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/033520b2d5e788f1. Report an issue: GitHub.