{"record":{"id":"5d126aef612be00f","repo":"puppeteer/puppeteer","slug":"mutationobserver-is-not-available-in-this-environm","errorCode":null,"errorMessage":"MutationObserver is not available in this environment.","messagePattern":"MutationObserver is not available in this environment\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/puppeteer-core/src/injected/TextContent.ts","lineNumber":80,"sourceCode":"  while (node) {\n    textContentCache.delete(node);\n    if (node instanceof ShadowRoot) {\n      node = node.host;\n    } else {\n      node = node.parentNode;\n    }\n  }\n};\n\n/**\n * Erases the cache when the tree has mutated text.\n */\nconst observedNodes = new WeakSet<Node>();\nlet textChangeObserver: MutationObserver;\nconst getTextChangeObserver = () => {\n  const MutationObserverImpl = globalThis.MutationObserver;\n  if (!MutationObserverImpl) {\n    throw new Error('MutationObserver is not available in this environment.');\n  }\n  if (!textChangeObserver) {\n    textChangeObserver = new MutationObserverImpl(mutations => {\n      for (const mutation of mutations) {\n        eraseFromCache(mutation.target);\n      }\n    });\n  }\n  return textChangeObserver;\n};\n\n/**\n * Builds the text content of a node using some custom logic.\n *\n * @remarks\n * The primary reason this function exists is due to {@link ShadowRoot}s not having\n * text content.\n *","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/puppeteer/puppeteer/blob/d484e21c17f6023826fefdcdeeb553e04a7aaed8/packages/puppeteer-core/src/injected/TextContent.ts#L62-L98","documentation":"TextContent (the injected text selector engine) lazily builds a MutationObserver to invalidate its cached text when the DOM mutates. If globalThis.MutationObserver is undefined when getTextChangeObserver() runs, it throws — the engine cannot safely cache text content without a way to observe mutations.","triggerScenarios":"Running the injected TextContent engine in a JS environment that lacks MutationObserver (very old browsers, minimal jsdom/happy-dom configurations, some workers). Triggered the first time a text selector ('::p-text(...)') is evaluated.","commonSituations":"Running Puppeteer's selector logic against a non-standard DOM (jsdom with MutationObserver disabled); old embedded browser views; headless shells stripped of the observer API.","solutions":["Target a browser/DOM runtime that implements MutationObserver (all evergreen browsers do).","If using jsdom/happy-dom in tests, enable their MutationObserver implementation.","Avoid text selectors ('text/', '::p-text') in environments known to lack MutationObserver.","Feature-detect globalThis.MutationObserver before relying on text selectors."],"exampleFix":"// before\nawait page.$('::p-text(Sign in)'); // throws if MutationObserver missing\n\n// after\nif (!globalThis.MutationObserver) {\n  // fall back to XPath or aria selectors that don't need the observer\n  await page.$('//button[contains(., \"Sign in\")]');\n}","handlingStrategy":"type-guard","validationCode":"function supportsTextSelector(): boolean {\n  return typeof globalThis.MutationObserver === 'function';\n}\nif (!supportsTextSelector()) {\n  // avoid '::p-text(...)' selectors; use xpath/aria instead\n}","typeGuard":"const hasMutationObserver = (): boolean =>\n  typeof globalThis.MutationObserver === 'function';","tryCatchPattern":"try {\n  await page.$('::p-text(Sign in)');\n} catch (e) {\n  if (e instanceof Error && e.message === 'MutationObserver is not available in this environment.') {\n    await page.$('//button[contains(., \"Sign in\")]'); // xpath fallback\n  } else throw e;\n}","preventionTips":["Target evergreen browsers that ship MutationObserver.","Enable MutationObserver in jsdom/happy-dom test configs.","Feature-detect before relying on text selectors."],"tags":["environment","dom","browser","selectors","text-selector"],"backgroundTag":null,"analyzedSha":"d484e21c17f6023826fefdcdeeb553e04a7aaed8","analyzedAt":"2026-08-12T06:33:19.665Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}