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
- Verify the CSS selector matches in the current DOM (document.querySelector in console)
- Wait for the element to render (waitFor selector or navigation settle) before snapshotting
- Omit the selector to snapshot the whole document
- Use the aria-ref handle path for element-scoped work instead of querySelector
- 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
- Test the CSS selector with document.querySelector before snapshotting
- Use waitFor to ensure the element exists first
- Remember the selector path is CSS-only — never pass aria-ref strings here
- Omit the selector for whole-document snapshots
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
- Element handle selector no longer resolves
- No element matched ${spec.raw}
- Browser selector must be a string; got ${kind}. tab.click/ty
- No page target matched ${JSON.stringify(options.matcher)}. A
- tab.select() requires a <select> element
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/74c14d7740450a49.
Report an issue: GitHub.