SeleniumHQ/selenium · error · TypeError

events should be string or string array

Error message

events should be string or string array

What it means

Thrown by the BiDi session.subscribe() method when the events argument is neither a string nor an array of strings. The library wraps the argument with an internal toArray() helper (which tolerates undefined and single values), then checks that every element is a string before sending the session.subscribe WebDriver BiDi command. A non-string element makes the protocol payload invalid, so the client rejects it before the network round-trip.

Source

Thrown at javascript/selenium-webdriver/bidi/index.js:255

  async subscribe(events, browsingContexts) {
    function toArray(arg) {
      if (arg === undefined) {
        return []
      }

      return Array.isArray(arg) ? [...arg] : [arg]
    }

    const eventsArray = toArray(events)
    const contextsArray = toArray(browsingContexts)

    const params = {
      method: 'session.subscribe',
      params: {},
    }

    if (eventsArray.length && eventsArray.some((event) => typeof event !== 'string')) {
      throw new TypeError('events should be string or string array')
    }

    if (contextsArray.length && contextsArray.some((context) => typeof context !== 'string')) {
      throw new TypeError('browsingContexts should be string or string array')
    }

    if (eventsArray.length) {
      params.params.events = eventsArray
    }

    if (contextsArray.length) {
      params.params.contexts = contextsArray
    }

    this.events.push(...eventsArray)

    await this.send(params)
  }

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Ensure every entry in the events argument is a string; pass either a single event name string or an array of event-name strings (e.g. subscribe('log.entryAdded') or subscribe(['log.entryAdded','browsingContext.navigationStarted'])).
  2. If events come from dynamic data, coerce/map them to strings before calling subscribe: events.map(String).
  3. Use the BiDi event constant objects exported by the library (e.g. NetworkEvent, BrowsingContextEvent) instead of hand-typing names, so values are guaranteed strings.

Example fix

// before
await bidi.subscribe([{ event: 'log.entryAdded' }], contextId)

// after
await bidi.subscribe('log.entryAdded', contextId)
// or an array of strings
await bidi.subscribe(['log.entryAdded', 'browsingContext.navigationStarted'], contextId)
Defensive patterns

Strategy: validation

Validate before calling

function assertStringEvents(events) {
  const arr = Array.isArray(events) ? events : events === undefined ? [] : [events]
  if (arr.length && arr.some((e) => typeof e !== 'string')) {
    throw new TypeError('events must be string or string[]')
  }
  return arr
}

Type guard

// events is string | string[] | undefined
function isStringOrStringArray(v) {
  if (v === undefined) return true
  if (typeof v === 'string') return true
  return Array.isArray(v) && v.every((e) => typeof e === 'string')
}

Try / catch

try {
  await bidi.subscribe(events, contexts)
} catch (e) {
  if (e instanceof TypeError && /events should be string/i.test(e.message)) {
    // normalize events and retry
  } else throw e
}

Prevention

When it happens

Trigger: Calling bidi.subscribe(events, browsingContexts) where events is a number, object, boolean, null, or an array containing any non-string element (e.g. subscribe(42), subscribe([{name:'x'}]), or subscribe(['log.entryAdded', 7])).

Common situations: Passing event identifiers from a config object that were not coerced to strings; copying event names from an enum that includes numeric codes; spreading a mixed array built from user input; migrating from a legacy API that accepted different event shapes.

Related errors


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