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

  1. Re-run the observe/snapshot after confirming the page has loaded real content (check page URL/main frame).
  2. Retry the snapshot after a short delay — transient null trees often resolve once navigation completes.
  3. Wait for a meaningful load state (e.g. waitForNavigation/load) before calling observe().
  4. 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

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


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