{"record":{"id":"237592ee565ea828","repo":"can1357/oh-my-pi","slug":"tab-select-requires-a-select-element-237592","errorCode":null,"errorMessage":"tab.select() requires a <select> element","messagePattern":"tab\\.select\\(\\) requires a <select> element","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-worker.ts","lineNumber":2033,"sourceCode":"\t\t}\n\t}\n\n\tasync #select(selector: string, values: string[], timeoutMs: number, signal: AbortSignal): Promise<string[]> {\n\t\tconst handle = await this.#resolveActionHandle(selector, timeoutMs, signal);\n\t\ttry {\n\t\t\treturn (await untilAborted(signal, () =>\n\t\t\t\thandle.evaluate((el, vals) => {\n\t\t\t\t\tinterface SelectOption {\n\t\t\t\t\t\tvalue: string;\n\t\t\t\t\t\tselected: boolean;\n\t\t\t\t\t}\n\t\t\t\t\tinterface SelectLike {\n\t\t\t\t\t\ttagName: string;\n\t\t\t\t\t\toptions: ArrayLike<SelectOption>;\n\t\t\t\t\t\tdispatchEvent: (event: unknown) => boolean;\n\t\t\t\t\t}\n\t\t\t\t\tconst select = el as unknown as SelectLike;\n\t\t\t\t\tif (select?.tagName !== \"SELECT\") throw new Error(\"tab.select() requires a <select> element\");\n\t\t\t\t\tconst EventCtor = (\n\t\t\t\t\t\tglobalThis as unknown as { Event: new (type: string, init?: { bubbles: boolean }) => unknown }\n\t\t\t\t\t).Event;\n\t\t\t\t\tconst wanted = new Set(vals as string[]);\n\t\t\t\t\t// Assign the full selection first, then read back: on a single\n\t\t\t\t\t// <select>, un-selecting the current option mid-loop leaves the\n\t\t\t\t\t// browser reporting it selected until another option takes over,\n\t\t\t\t\t// which double-counted the old value in the returned list.\n\t\t\t\t\tfor (let i = 0; i < select.options.length; i++) {\n\t\t\t\t\t\tconst opt = select.options[i] as SelectOption;\n\t\t\t\t\t\topt.selected = wanted.has(opt.value);\n\t\t\t\t\t}\n\t\t\t\t\tconst selected: string[] = [];\n\t\t\t\t\tfor (let i = 0; i < select.options.length; i++) {\n\t\t\t\t\t\tconst opt = select.options[i] as SelectOption;\n\t\t\t\t\t\tif (opt.selected) selected.push(opt.value);\n\t\t\t\t\t}\n\t\t\t\t\tselect.dispatchEvent(new EventCtor(\"input\", { bubbles: true }));","sourceCodeStart":2015,"sourceCodeEnd":2051,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-worker.ts#L2015-L2051","documentation":"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>.","triggerScenarios":"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>.","commonSituations":"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.","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."],"exampleFix":"// before\nawait tab.select(\"#react-select-country\", [\"US\"]); // div-based dropdown\n// after\nawait tab.click(\"#react-select-country\");\nawait tab.click(\"#react-select-country-option-us\");","handlingStrategy":"validation","validationCode":"const tag = await tab.evaluate((sel) => document.querySelector(sel)?.tagName ?? null, selector);\nif (tag !== \"SELECT\") throw new Error(`${selector} is <${tag}>; use click-based flow for custom dropdowns`);","typeGuard":"function isNativeSelect(info: { tagName?: string }): boolean {\n  return info?.tagName === \"SELECT\";\n}","tryCatchPattern":"try {\n  await tab.select(selector, values);\n} catch (err) {\n  if (err.message.includes(\"requires a <select> element\")) {\n    await tab.click(selector); // open custom dropdown\n    return tab.click(optionSelectorFor(values[0]));\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["browser","select","dom","wrong-element-type"],"backgroundTag":"wrong-element-type-for-action","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}