can1357/oh-my-pi · error · ToolError

The attached browser tab is not visible; switch to it before

Error message

The attached browser tab is not visible; switch to it before taking a screenshot

What it means

Before taking a screenshot of an attached (user-visible) browser tab, the worker checks `document.visibilityState === "visible"` in the page. Attached tabs can be hidden (background tab, minimized window, occluded); screenshots of hidden tabs would be blank, so the tool throws and asks the user to switch to the tab first.

Source

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

export function describeScreenshot(opts?: ScreenshotOptions): string {
	if (opts?.selector) return `tab.screenshot({ selector: ${JSON.stringify(opts.selector)} })`;
	if (opts?.fullPage) return "tab.screenshot({ fullPage: true })";
	return "tab.screenshot()";
}
export async function preparePageForScreenshot(
	page: Pick<Page, "bringToFront" | "evaluate">,
	signal: AbortSignal | undefined,
	activate: boolean,
): Promise<void> {
	if (activate) {
		await untilAborted(signal, () => page.bringToFront()).catch(() => undefined);
		return;
	}
	const visible = await untilAborted(signal, () => page.evaluate(() => document.visibilityState === "visible")).catch(
		() => false,
	);
	if (!visible) {
		throw new ToolError("The attached browser tab is not visible; switch to it before taking a screenshot");
	}
}

/** Summarize still-running helpers (oldest first) so a cell timeout names what stalled. */
export function describeInflight(inflight: Map<number, InflightOp>): string {
	const now = Date.now();
	return [...inflight.values()]
		.sort((a, b) => a.startedAt - b.startedAt)
		.map(op => `${op.label} (${((now - op.startedAt) / 1000).toFixed(1)}s)`)
		.join(", ");
}

export class WorkerCore {
	#transport: Transport;
	#browser?: Browser;
	#page?: Page;
	#targetId?: string;
	#elementCache = new Map<number, ElementHandle>();

View on GitHub (pinned to 9690622007)

Solutions

  1. Switch to the target tab (bring its window to front / activate the tab) and retry the screenshot.
  2. Run the browser headless instead of attaching to a visible browser — headless pages report `visible`.
  3. Structure the workflow to minimize tab switching, or notify the user to keep the tab focused during screenshots.
  4. Handle the error in the automation by using snapshot/observe (DOM-based) instead of a pixel screenshot when the tab can't be focused.

Example fix

// before
await tab.screenshot(); // tab is in background
// after
await tab.activate(); // bring tab to front
await tab.screenshot();
Defensive patterns

Strategy: fallback

Validate before calling

const visible = await page.evaluate(() => document.visibilityState === "visible").catch(() => false);
if (!visible) await tab.activate(); // bring tab to front first

Try / catch

try {
  await tab.screenshot();
} catch (err) {
  if (err instanceof ToolError && err.message.includes("not visible")) {
    return tab.ariaSnapshot(); // DOM-based fallback when focus can't be given
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling screenshot on a tab attached to the user's real browser while that tab is in a background tab, a minimized window, or otherwise not the active tab — `visibilityState` is `hidden`.

Common situations: User opened the automated tab then switched away mid-run; OS-level window minimization; multi-window setups where the tool operates on a non-focused window.

Related errors


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