{"record":{"id":"7b6ca9af79f52611","repo":"facebook/react","slug":"409","errorCode":"409","errorMessage":"Cannot update an unmounted root.","messagePattern":"Cannot update an unmounted root\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-dom/src/client/ReactDOMRoot.js","lineNumber":112,"sourceCode":"  defaultOnUncaughtError,\n  defaultOnCaughtError,\n  defaultOnRecoverableError,\n} from 'react-reconciler/src/ReactFiberReconciler';\nimport {defaultOnDefaultTransitionIndicator} from './ReactDOMDefaultTransitionIndicator';\nimport {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags';\n\n// $FlowFixMe[missing-this-annot]\nfunction ReactDOMRoot(internalRoot: FiberRoot) {\n  this._internalRoot = internalRoot;\n}\n\n// $FlowFixMe[prop-missing] found when upgrading Flow\nReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =\n  // $FlowFixMe[missing-this-annot]\n  function (children: ReactNodeList): void {\n    const root = this._internalRoot;\n    if (root === null) {\n      throw new Error('Cannot update an unmounted root.');\n    }\n\n    if (__DEV__) {\n      // using a reference to `arguments` bails out of GCC optimizations which affect function arity\n      const args = arguments;\n      if (typeof args[1] === 'function') {\n        console.error(\n          'does not support the second callback argument. ' +\n            'To execute a side effect after rendering, declare it in a component body with useEffect().',\n        );\n      } else if (isValidContainer(args[1])) {\n        console.error(\n          'You passed a container to the second argument of root.render(...). ' +\n            \"You don't need to pass it again since you already passed it to create the root.\",\n        );\n      } else if (typeof args[1] !== 'undefined') {\n        console.error(\n          'You passed a second argument to root.render(...) but it only accepts ' +","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-dom/src/client/ReactDOMRoot.js#L94-L130","documentation":"Root objects returned by createRoot/hydrateRoot keep their FiberRoot in _internalRoot, which root.unmount() sets to null. Every subsequent root.render(...) reads that field first and throws error 409 when it is null, because React cannot revive an unmounted root. The usual cause is code holding a stale root reference and using it after cleanup.","triggerScenarios":"Calling root.render() or root.unmount() again after root.unmount() — an event handler, interval, or pending promise that still fires after the app was torn down; double-invoked effect cleanup that unmounts while an async callback later calls root.render.","commonSituations":"Micro-frontend mount/unmount lifecycles where a global update function keeps running; tests that unmount in afterEach while pending work later renders; polling or subscription code capturing the root.","solutions":["Track unmount state in a wrapper (set root = null on unmount) and check it before every render","Stop timers, listeners, and subscriptions that may call render after unmount (clearInterval, AbortController)","If you must remount, create a fresh createRoot(container) instead of reusing the old root","In tests, await pending async work before unmounting in cleanup"],"exampleFix":"// before\nroot.unmount();\n// later...\nroot.render(<App />); // throws\n\n// after\nroot.unmount();\nroot = null;\n// later...\nif (root) {\n  root.render(<App />);\n} else {\n  root = createRoot(container);\n  root.render(<App />);\n}","handlingStrategy":"validation","validationCode":"class AppHost {\n  root = null;\n  mount(container, element) {\n    if (!this.root) this.root = createRoot(container);\n    this.root.render(element);\n  }\n  update(element) {\n    if (this.root === null) return; // unmounted — skip instead of throwing\n    this.root.render(element);\n  }\n  unmount() {\n    this.root?.unmount();\n    this.root = null;\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wrap roots in an object that nulls the reference on unmount and checks it before every render","Cancel timers, listeners, and in-flight requests during teardown so no callback renders later","Never cache root objects across micro-frontend remount cycles; create fresh roots instead","In tests, flush pending work (await act) before unmount cleanup"],"tags":["react-dom","createroot","unmount","lifecycle","stale-reference"],"backgroundTag":"render-after-root-unmount","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}