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
- Switch to the target tab (bring its window to front / activate the tab) and retry the screenshot.
- Run the browser headless instead of attaching to a visible browser — headless pages report `visible`.
- Structure the workflow to minimize tab switching, or notify the user to keep the tab focused during screenshots.
- 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
- Activate the tab before screenshots of attached browsers
- Prefer headless mode when pixel output is needed and focus can't be guaranteed
- Use ariaSnapshot/observe instead of screenshots when the tab may be backgrounded
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
- cmux browser screenshot response did not include png_base64
- Drag selector did not resolve to a visible element: ${target
- Playwright-only selector ${JSON.stringify(selector)} is not
- Unsupported selector prefix. Use CSS or puppeteer query hand
- ${label} cannot run: this handle was invalidated after ${sta
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/431c0174da5d1ce1.
Report an issue: GitHub.