can1357/oh-my-pi · error · ToolError
Accessibility snapshot unavailable
Error message
Accessibility snapshot unavailable
What it means
tab.observe()/snapshot calls page.accessibility.snapshot({ interestingOnly }) to build the element observation list. If the browser returns a null accessibility tree, the worker throws this ToolError because there is nothing to enumerate. A null tree means the page's accessibility tree could not be computed (empty/blank page, unsupported target, or the tree was disabled/being rebuilt).
Source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:1847
},
id: async id => enrich(await this.#resolveCachedHandle(id)),
ref: async id => enrich(await this.#resolveAriaRef(id)),
};
}
async #collectObservation(options: {
includeAll?: boolean;
viewportOnly?: boolean;
signal?: AbortSignal;
}): Promise<Observation> {
const page = this.#requirePage();
this.#clearElementCache();
const includeAll = options.includeAll ?? false;
const viewportOnly = options.viewportOnly ?? false;
const snapshot = (await untilAborted(options.signal, () =>
page.accessibility.snapshot({ interestingOnly: !includeAll }),
)) as SerializedAXNode | null;
if (!snapshot) throw new ToolError("Accessibility snapshot unavailable");
const entries: ObservationEntry[] = [];
await collectObservationEntries(this, snapshot, entries, { includeAll, viewportOnly });
const scroll = (await untilAborted(options.signal, () =>
page.evaluate(() => {
const win = globalThis as unknown as {
scrollX: number;
scrollY: number;
innerWidth: number;
innerHeight: number;
document: { documentElement: { scrollWidth: number; scrollHeight: number } };
};
const doc = win.document.documentElement;
return {
x: win.scrollX,
y: win.scrollY,
width: win.innerWidth,
height: win.innerHeight,
scrollWidth: doc.scrollWidth,View on GitHub (pinned to 9690622007)
Solutions
- Re-run the observe/snapshot after confirming the page has loaded real content (check page URL/main frame).
- Retry the snapshot after a short delay — transient null trees often resolve once navigation completes.
- Wait for a meaningful load state (e.g. waitForNavigation/load) before calling observe().
- If the tab is dead, close it and open a fresh tab, then observe again.
Example fix
// before
const snapshot = await page.accessibility.snapshot({ interestingOnly: true });
// after
await page.waitForNavigation({ waitUntil: "load" }).catch(() => undefined);
const snapshot = await page.accessibility.snapshot({ interestingOnly: true });
if (!snapshot) throw new Error("page has no accessibility tree yet — retry after load"); Defensive patterns
Strategy: retry
Validate before calling
const url = await tab.evaluate(() => location.href);
if (!url || url === "about:blank") throw new Error("navigate to a real page before observe()"); Type guard
function hasAxTree(s: unknown): s is { role: string; children?: unknown[] } {
return !!s && typeof s === "object" && "role" in s;
} Try / catch
try {
const obs = await tab.observe({ signal });
} catch (err) {
if (err.message.includes("Accessibility snapshot unavailable")) {
await Bun.sleep(500);
return retryObserve();
}
throw err;
} Prevention
- Wait for page load/navigation to settle before observing
- Check location.href is not about:blank before calling observe
- Treat one null snapshot as transient and retry once
- Keep the tab alive — avoid navigating or closing it while observing
When it happens
Trigger: Calling tab.observe() (or any snapshot with includeAll/viewportOnly options) on a page whose accessibility snapshot returns null: a blank or about:blank tab, a crashed renderer, a page that closed mid-snapshot, or a target where Playwright/Puppeteer cannot enable the accessibility domain.
Common situations: Observing a tab that navigated to about:blank or an empty document; the tab was closed by the site between command dispatch and snapshot; headless browser attached to a page with accessibility tree suppressed; snapshot raced with a navigation that reset the DOM.
Related errors
- No sessions found for ${cwd}. Pass a session file or id.
- RPC message snapshot does not match current messages
- Browser selector must be a string; got ${kind}. tab.click/ty
- tab.ariaSnapshot: selector ${__sel} matched no element
- Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !==
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/73c781a68f7be12d.
Report an issue: GitHub.