jsdom/jsdom · error · TypeError

The options object must set at least one of 'attributes', 'c

Error message

The options object must set at least one of 'attributes', 'characterData', or 'childList' to true.

What it means

MutationObserver.observe() requires the options dict to request at least one observation type. jsdom validates options before registering the observer and throws this TypeError when `childList`, `attributes`, and `characterData` are all falsy, meaning the observer would never deliver any record.

Source

Thrown at lib/jsdom/living/mutation-observer/MutationObserver-impl.js:38

    this._callback = callback;
    this._nodeList = new IterableWeakList();
    this._recordQueue = [];

    this._id = ++mutationObserverId;
  }

  // https://dom.spec.whatwg.org/#dom-mutationobserver-observe
  observe(target, options) {
    if (("attributeOldValue" in options || "attributeFilter" in options) && !("attributes" in options)) {
      options.attributes = true;
    }

    if ("characterDataOldValue" in options & !("characterData" in options)) {
      options.characterData = true;
    }

    if (!options.childList && !options.attributes && !options.characterData) {
      throw new TypeError("The options object must set at least one of 'attributes', 'characterData', or 'childList' " +
        "to true.");
    } else if (options.attributeOldValue && !options.attributes) {
      throw new TypeError("The options object may only set 'attributeOldValue' to true when 'attributes' is true or " +
        "not present.");
    } else if (("attributeFilter" in options) && !options.attributes) {
      throw new TypeError("The options object may only set 'attributeFilter' when 'attributes' is true or not " +
        "present.");
    } else if (options.characterDataOldValue && !options.characterData) {
      throw new TypeError("The options object may only set 'characterDataOldValue' to true when 'characterData' is " +
        "true or not present.");
    }

    const existingRegisteredObserver = target._registeredObserverList.find(registeredObserver => {
      return registeredObserver.observer === this;
    });

    if (existingRegisteredObserver) {
      for (const node of this._nodeList) {

View on GitHub (pinned to 904cc9cd24)

Solutions

  1. Set at least one observation flag to true: `{ childList: true }`, `{ attributes: true }`, or `{ characterData: true }`.
  2. Log the options object right before observe() to confirm actual key names and boolean values.
  3. Fix any string-to-boolean conversion so values are real booleans, not 'true'/'false' strings.
  4. Default missing flags at call sites: `const opts = { childList: true, ...options };` only if intentional.

Example fix

// before
observer.observe(target, {}); // TypeError

// after
observer.observe(target, { childList: true, subtree: true });
Defensive patterns

Strategy: validation

Validate before calling

function validObserveOptions(o = {}) {
  return Boolean(o.childList) || Boolean(o.attributes) || Boolean(o.characterData);
}
if (!validObserveOptions(options)) throw new TypeError('observe options must enable childList, attributes, or characterData');

Type guard

function hasObservationFlag(o) {
  return o != null && ['childList', 'attributes', 'characterData'].some(k => o[k] === true);
}

Try / catch

try {
  observer.observe(target, options);
} catch (e) {
  if (e instanceof TypeError && /at least one of/.test(e.message)) {
    observer.observe(target, { ...options, childList: true });
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: `observer.observe(target, {})`; passing options whose values are the strings 'false'/'undefined' or 0 rather than booleans; building options dynamically and ending with all three keys false or absent.

Common situations: Serializing options from config/JSON where booleans became strings; spreading a partial options object that omits all three keys; refactoring that renamed a key (e.g. `child` instead of `childList`).

Related errors


AI-assisted analysis of jsdom/jsdom@904cc9cd24 (2026-09-01). Data as JSON: /api/errors/749c37d2bdd0b938. Report an issue: GitHub.