can1357/oh-my-pi · error

tab.ariaSnapshot: selector ${__sel} matched no element

Error message

tab.ariaSnapshot: selector ${__sel} matched no element

What it means

buildAriaSnapshotScript generates an in-page script for tab.ariaSnapshot. When a selector is provided, the script resolves it with document.querySelector (CSS only) and throws "tab.ariaSnapshot: selector <sel> matched no element" inside the page if nothing matches. The error surfaces through the tool as a ToolError/evaluation failure.

Source

Thrown at packages/coding-agent/src/tools/browser/aria/aria-snapshot.ts:130

			return /^e\d+$/.test(id) ? id : null;
		}
	}
	const bare = /^@?(e\d+)$/.exec(trimmed);
	return bare ? bare[1]! : null;
}

/**
 * Build a self-contained expression script that runs the vendored bundle in the
 * page and returns the ARIA snapshot YAML. Used by the cmux backend, whose
 * `browser.eval` RPC takes a script string and returns the completion value (it
 * has no ElementHandle to pass in). The script resolves `selector` via
 * `document.querySelector` in-page (CSS selectors only) or falls back to the
 * whole document. Like the puppeteer path it installs nothing on `window`.
 */
export function buildAriaSnapshotScript(selector: string | undefined, options: AriaSnapshotOptions = {}): string {
	const request = { depth: options.depth, boxes: options.boxes };
	const sel = selector ? JSON.stringify(selector) : "null";
	return `(function(){var module={exports:{}};\n${ariaBundle}\nvar __sel=${sel};var __root=__sel?document.querySelector(__sel):null;if(__sel&&!__root)throw new Error("tab.ariaSnapshot: selector "+__sel+" matched no element");return module.exports.ariaSnapshot(__root,${JSON.stringify(request)});})()`;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the CSS selector matches in the current DOM (document.querySelector in console)
  2. Wait for the element to render (waitFor selector or navigation settle) before snapshotting
  3. Omit the selector to snapshot the whole document
  4. Use the aria-ref handle path for element-scoped work instead of querySelector
  5. Check for iframes — the selector runs in the main frame document only

Example fix

// before
await tab.ariaSnapshot({ selector: "aria-ref=e5" }); // querySelector fails
// after
await tab.waitFor(".results");
await tab.ariaSnapshot({ selector: ".results" });
Defensive patterns

Strategy: validation

Validate before calling

const el = await tab.evaluate(`!!document.querySelector(${JSON.stringify(sel)})`);
if (!el) throw new Error(`selector ${sel} not in DOM yet — wait first`);
await tab.ariaSnapshot({ selector: sel });

Try / catch

try {
  await tab.ariaSnapshot({ selector });
} catch (e) {
  if (e instanceof ToolError && e.message.includes("matched no element")) {
    await tab.waitFor(selector); // wait then retry
    return await tab.ariaSnapshot({ selector });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling tab.ariaSnapshot({ selector: "#foo" }) when no element matching the CSS selector exists in the document at evaluation time; passing an aria-ref string ("aria-ref=eN") as the selector — querySelector only understands CSS.

Common situations: Typos in CSS selectors; element not yet rendered (page still loading or SPA not mounted); element inside a different frame/document; passing an aria-ref where a CSS selector is required.

Related errors


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