can1357/oh-my-pi · error · Error
No element matched ${spec.raw}
Error message
No element matched ${spec.raw} What it means
CmuxTab's selector actions build an in-page script that calls findElement(spec); when no element matches the selector spec, the page side throws 'No element matched <spec.raw>', surfaced as this ToolError. This is the generic 'selector did not match anything' error for click/fill/type/check/select/exists-style actions on the cmux surface.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts:910
await this.#request("browser.scroll_into_view", { selector: nativeSelector });
return undefined as TResult;
}
}
return await this.#evalSelectorAction<TResult>(spec, action, args);
}
async #evalSelectorAction<TResult>(
spec: SelectorSpec,
action: string,
args: Record<string, unknown>,
): Promise<TResult> {
const script = `(() => {
const spec = ${JSON.stringify(spec)};
const action = ${JSON.stringify(action)};
const args = ${JSON.stringify(args)};
${PAGE_SELECTOR_HELPERS}
const element = findElement(spec);
if (!element) throw new Error("No element matched " + spec.raw);
if (action !== "exists") element.scrollIntoView({ block: "center", inline: "center" });
switch (action) {
case "click":
mouseEvent(element, "mousedown");
mouseEvent(element, "mouseup");
if (typeof element.click === "function") element.click();
else mouseEvent(element, "click");
return true;
case "dblclick":
mouseEvent(element, "dblclick");
return true;
case "hover":
mouseEvent(element, "mouseover");
mouseEvent(element, "mouseenter");
mouseEvent(element, "mousemove");
return true;
case "focus":
if (typeof element.focus === "function") element.focus();View on GitHub (pinned to 9690622007)
Solutions
- Wait for the element first: await tab.waitForSelector(sel) (with a generous timeout) before performing the action.
- Verify the selector matches by testing it in-page: tab.evaluate((s) => !!document.querySelector(s), sel), and adjust (prefer data-testid or id).
- Check whether the element lives in an iframe or shadow root — switch to the right frame or use a deep/role-based selector the helpers support.
- Re-check for typos and dynamic text: copy the exact visible label/attribute from a live tab.evaluate(document.body.innerHTML) snapshot.
Example fix
// before: act immediately, element not yet mounted
await tab.fill("#email", "user@example.com");
// after: wait for the element, then act
await tab.waitForSelector("#email", { timeout: 10_000 });
await tab.fill("#email", "user@example.com"); Defensive patterns
Strategy: validation
Validate before calling
const matches = await tab.evaluate(
(sel) => document.querySelectorAll(sel).length,
"#email",
);
if (matches === 0) await tab.waitForSelector("#email", { timeout: 10_000 }); Try / catch
try {
await tab.fill("#email", "user@example.com");
} catch (err) {
if (err instanceof ToolError && err.message.startsWith("No element matched")) {
await tab.waitForSelector("#email", { timeout: 10_000 });
await tab.fill("#email", "user@example.com");
} else throw err;
} Prevention
- waitForSelector before every selector-based action on dynamically rendered pages
- Validate selectors in-page with querySelector before scripting around them
- Use stable attributes (data-testid, id, aria roles) instead of hashed classes or visible text
- Check for iframe/shadow-DOM boundaries the selector helpers cannot cross
When it happens
Trigger: Calling tab.click/tab.fill/tab.type/tab.select/tab.check with a CSS selector, text selector, or role spec that matches no element at action time: typo in the selector, element inside an iframe not reachable by the helper, element not yet rendered, wrong frame/page, or selector depending on auto-generated attributes that changed.
Common situations: Selectors written for a different DOM state (before login, before modal opens); framework class-name hashing changing between environments; elements in shadow DOM or iframes the PAGE_SELECTOR_HELPERS don't traverse; React 18 concurrent rendering delaying mount past the action; using text-match selectors whose label text changed (i18n, whitespace).
Related errors
- tab.ariaSnapshot: selector ${__sel} matched no element
- Element handle selector no longer resolves
- Screenshot selector did not resolve to an element
- Drag ${role} selector did not resolve: ${target}
- Browser selector must be a string; got ${kind}. tab.click/ty
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e5b2faa465a5d100.
Report an issue: GitHub.