can1357/oh-my-pi · error · Error
tab.select() requires a <select> element
Error message
tab.select() requires a <select> element
What it means
tab.select() resolves the target selector to an element and then checks tagName === "SELECT" inside the page. Selecting options only makes sense on a <select>; on any other tag the in-page evaluate throws this error, which surfaces as the tool failure. It is thrown even if the element supports a role="combobox" ARIA pattern but is not a native <select>.
Source
Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:2033
}
}
async #select(selector: string, values: string[], timeoutMs: number, signal: AbortSignal): Promise<string[]> {
const handle = await this.#resolveActionHandle(selector, timeoutMs, signal);
try {
return (await untilAborted(signal, () =>
handle.evaluate((el, vals) => {
interface SelectOption {
value: string;
selected: boolean;
}
interface SelectLike {
tagName: string;
options: ArrayLike<SelectOption>;
dispatchEvent: (event: unknown) => boolean;
}
const select = el as unknown as SelectLike;
if (select?.tagName !== "SELECT") throw new Error("tab.select() requires a <select> element");
const EventCtor = (
globalThis as unknown as { Event: new (type: string, init?: { bubbles: boolean }) => unknown }
).Event;
const wanted = new Set(vals as string[]);
// Assign the full selection first, then read back: on a single
// <select>, un-selecting the current option mid-loop leaves the
// browser reporting it selected until another option takes over,
// which double-counted the old value in the returned list.
for (let i = 0; i < select.options.length; i++) {
const opt = select.options[i] as SelectOption;
opt.selected = wanted.has(opt.value);
}
const selected: string[] = [];
for (let i = 0; i < select.options.length; i++) {
const opt = select.options[i] as SelectOption;
if (opt.selected) selected.push(opt.value);
}
select.dispatchEvent(new EventCtor("input", { bubbles: true }));View on GitHub (pinned to 9690622007)
Solutions
- Target the actual native <select> element if one exists in the DOM (inspect with DevTools).
- For custom dropdowns, click to open the list and click the option element instead of tab.select().
- Use the aria-ref of a real <select> from a fresh tab.observe().
- As a fallback, set the value via page evaluate and dispatch a change event if the site tolerates it.
Example fix
// before
await tab.select("#react-select-country", ["US"]); // div-based dropdown
// after
await tab.click("#react-select-country");
await tab.click("#react-select-country-option-us"); Defensive patterns
Strategy: validation
Validate before calling
const tag = await tab.evaluate((sel) => document.querySelector(sel)?.tagName ?? null, selector);
if (tag !== "SELECT") throw new Error(`${selector} is <${tag}>; use click-based flow for custom dropdowns`); Type guard
function isNativeSelect(info: { tagName?: string }): boolean {
return info?.tagName === "SELECT";
} Try / catch
try {
await tab.select(selector, values);
} catch (err) {
if (err.message.includes("requires a <select> element")) {
await tab.click(selector); // open custom dropdown
return tab.click(optionSelectorFor(values[0]));
}
throw err;
} Prevention
- Inspect the DOM to confirm a native <select> exists before using tab.select()
- For component-library dropdowns, use click open + click option
- Prefer aria-refs of real select elements from a fresh observe
- Document that tab.select() only supports native selects
When it happens
Trigger: tab.select("#country", ["US"]) where #country is a div-based custom dropdown, an <input> with a datalist, or an ARIA listbox built from <ul>/<li>.
Common situations: Modern component libraries (React-Select, headless UI) render fake dropdowns from divs; targeting the wrapper element rather than the native select; a site replaced a native select with a custom widget in a redesign.
Related errors
- tab.uploadFile() requires an <input type="file"> element (go
- tab.ariaSnapshot: selector ${__sel} matched no element
- Element handle selector no longer resolves
- No element matched ${spec.raw}
- Element id ${id} is stale. Run tab.observe() again.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/237592ee565ea828.
Report an issue: GitHub.