diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js
index 83633532dca4a..cd00c561b6320 100644
--- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js
@@ -320,4 +320,14 @@ describe('ReactDOMRoot', () => {
flush();
expect(container.textContent).toEqual('1');
});
+
+ it('handles fatal errors triggered by batch.commit()', () => {
+ const root = ReactDOM.createRoot(container);
+ const batch = root.createBatch();
+ const InvalidType = undefined;
+ expect(() => batch.render()).toWarnDev([
+ 'React.createElement: type is invalid',
+ ]);
+ expect(() => batch.commit()).toThrow('Element type is invalid');
+ });
});
diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js
index ec4c554182eab..716f963b9df0b 100644
--- a/packages/react-reconciler/src/ReactFiberScheduler.js
+++ b/packages/react-reconciler/src/ReactFiberScheduler.js
@@ -884,7 +884,12 @@ export default function(
const sourceFiber: Fiber = nextUnitOfWork;
let returnFiber = sourceFiber.return;
if (returnFiber === null) {
- // This is a fatal error.
+ // This is the root. The root could capture its own errors. However,
+ // we don't know if it errors before or after we pushed the host
+ // context. This information is needed to avoid a stack mismatch.
+ // Because we're not sure, treat this as a fatal error. We could track
+ // which phase it fails in, but doesn't seem worth it. At least
+ // for now.
didFatal = true;
onUncaughtError(thrownValue);
break;
@@ -1444,6 +1449,8 @@ export default function(
// Perform work on root as if the given expiration time is the current time.
// This has the effect of synchronously flushing all work up to and
// including the given time.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = expirationTime;
performWorkOnRoot(root, expirationTime, false);
finishRendering();
}