{"record":{"id":"31d85c922c2c991b","repo":"jsdom/jsdom","slug":"the-options-object-may-only-set-characterdataoldv","errorCode":null,"errorMessage":"The options object may only set 'characterDataOldValue' to true when 'characterData' is true or not present.","messagePattern":"The options object may only set 'characterDataOldValue' to true when 'characterData' is true or not present\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/jsdom/living/mutation-observer/MutationObserver-impl.js","lineNumber":47,"sourceCode":"    if ((\"attributeOldValue\" in options || \"attributeFilter\" in options) && !(\"attributes\" in options)) {\n      options.attributes = true;\n    }\n\n    if (\"characterDataOldValue\" in options & !(\"characterData\" in options)) {\n      options.characterData = true;\n    }\n\n    if (!options.childList && !options.attributes && !options.characterData) {\n      throw new TypeError(\"The options object must set at least one of 'attributes', 'characterData', or 'childList' \" +\n        \"to true.\");\n    } else if (options.attributeOldValue && !options.attributes) {\n      throw new TypeError(\"The options object may only set 'attributeOldValue' to true when 'attributes' is true or \" +\n        \"not present.\");\n    } else if ((\"attributeFilter\" in options) && !options.attributes) {\n      throw new TypeError(\"The options object may only set 'attributeFilter' when 'attributes' is true or not \" +\n        \"present.\");\n    } else if (options.characterDataOldValue && !options.characterData) {\n      throw new TypeError(\"The options object may only set 'characterDataOldValue' to true when 'characterData' is \" +\n        \"true or not present.\");\n    }\n\n    const existingRegisteredObserver = target._registeredObserverList.find(registeredObserver => {\n      return registeredObserver.observer === this;\n    });\n\n    if (existingRegisteredObserver) {\n      for (const node of this._nodeList) {\n        node._registeredObserverList = node._registeredObserverList.filter(registeredObserver => {\n          return registeredObserver.source !== existingRegisteredObserver;\n        });\n      }\n\n      existingRegisteredObserver.options = options;\n    } else {\n      target._registeredObserverList.push({\n        observer: this,","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/jsdom/jsdom/blob/904cc9cd2464e959b739910cb046afe57f7a1922/lib/jsdom/living/mutation-observer/MutationObserver-impl.js#L29-L65","documentation":"`characterDataOldValue` requests old character-data values, which is only meaningful when character-data mutations are observed. jsdom throws this TypeError when `characterDataOldValue` is truthy but `characterData` is not explicitly true, per the MutationObserver spec's validation steps.","triggerScenarios":"`observer.observe(target, { characterDataOldValue: true })` without `characterData: true`; observing with `{ subtree: true, characterDataOldValue: true }` assuming subtree enables character data (it does not); merged options where characterData was lost.","commonSituations":"Believing subtree: true plus characterDataOldValue is sufficient; config templates listing characterDataOldValue but not characterData; refactors renaming characterData to textChanges.","solutions":["Add `characterData: true` alongside `characterDataOldValue: true`.","Derive it: `if (options.characterDataOldValue) options.characterData = true;` before observe().","Remember `subtree: true` does not replace the characterData flag; set both.","Remove characterDataOldValue if old values are not needed."],"exampleFix":"// before\nobserver.observe(target, { subtree: true, characterDataOldValue: true }); // TypeError\n\n// after\nobserver.observe(target, { subtree: true, characterData: true, characterDataOldValue: true });","handlingStrategy":"validation","validationCode":"if (options.characterDataOldValue && options.characterData !== true) {\n  options.characterData = true;\n}","typeGuard":"function hasValidCharacterDataOptions(o) {\n  return !o.characterDataOldValue || o.characterData === true;\n}","tryCatchPattern":"try {\n  observer.observe(target, options);\n} catch (e) {\n  if (e instanceof TypeError && /characterDataOldValue/.test(e.message)) {\n    observer.observe(target, { ...options, characterData: true });\n  } else {\n    throw e;\n  }\n}","preventionTips":["Pair characterDataOldValue: true with characterData: true.","Remember subtree: true does not enable characterData; set it explicitly.","Validate observer options with a schema helper before calling observe()."],"tags":["mutation-observer","typeerror","options-validation"],"backgroundTag":"invalid-mutation-observer-options","analyzedSha":"904cc9cd2464e959b739910cb046afe57f7a1922","analyzedAt":"2026-09-01T11:17:47.630Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}