SeleniumHQ/selenium · error · Error

Select only works on <select> elements

Error message

Select only works on <select> elements

What it means

Thrown inside a `.then()` callback in the Select constructor when the element's tagName is not 'select'. IMPORTANT: the throw occurs inside an un-awaited promise — the constructor does not return or await it, so the rejection becomes an unhandled promise rejection (or surfaces later, detached from the call site). Users may see a confusing async error rather than a clean constructor failure.

Source

Thrown at javascript/selenium-webdriver/lib/select.js:157

/**
 * @implements ISelect
 */
class Select {
  /**
   * Create an Select Element
   * @param {WebElement} element Select WebElement.
   */
  constructor(element) {
    if (element === null) {
      throw new Error(`Element must not be null. Please provide a valid <select> element.`)
    }

    this.element = element

    this.element.getAttribute('tagName').then(function (tagName) {
      if (tagName.toLowerCase() !== 'select') {
        throw new Error(`Select only works on <select> elements`)
      }
    })

    this.element.getAttribute('multiple').then((multiple) => {
      this.multiple = multiple !== null && multiple !== 'false'
    })
  }

  /**
   *
   * Select option with specified index.
   *
   * <example>
   <select id="selectbox">
   <option value="1">Option 1</option>
   <option value="2">Option 2</option>
   <option value="3">Option 3</option>
   </select>

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Verify the tag before constructing: const tag = await el.getTagName(); if (tag !== 'select') throw new Error('not a select');
  2. Use a locator scoped to selects: By.css('select#myid') so the wrong tag cannot match.
  3. Because the constructor's check is async and detached, perform your own synchronous-able pre-check and don't rely on the constructor to validate.
  4. Register a global unhandledRejection handler so a missed check surfaces loudly during dev.

Example fix

// before
const el = await driver.findElement(By.id('qty')); // a <div id="qty">
const sel = new Select(el); // async unhandled rejection

// after
const el = await driver.findElement(By.css('select#qty'));
// or guard:
if ((await el.getTagName()) !== 'select') throw new Error('element is not a <select>');
const sel = new Select(el);
Defensive patterns

Strategy: validation

Validate before calling

async function safeSelect(el) {
  const tag = (await el.getTagName()).toLowerCase();
  if (tag !== 'select') {
    throw new Error(`expected <select>, got <${tag}>`);
  }
  return new Select(el);
}

Type guard

async function isSelectElement(el) {
  return (await el.getTagName()).toLowerCase() === 'select';
}

Try / catch

process.on('unhandledRejection', (reason) => {
  if (reason instanceof Error && /Select only works on/.test(reason.message)) {
    log.error('Select created on a non-select element');
  }
});
// Prefer pre-validation rather than catching this detached rejection.

Prevention

When it happens

Trigger: new Select(divElement); new Select(textareaElement); new Select(inputElement); any element whose tagName.toLowerCase() !== 'select'.

Common situations: Locating an element by an id that exists but is a different tag; generic form helpers that wrap any element in Select; copied locators that match the wrong element after a DOM refactor.

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/35b1c6b9c1a425e4. Report an issue: GitHub.