{"record":{"id":"8ac208fb3c6b6068","repo":"facebook/react","slug":"345","errorCode":"345","errorMessage":"Root did not complete. This is a bug in React.","messagePattern":"Root did not complete\\. This is a bug in React\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-reconciler/src/ReactFiberWorkLoop.js","lineNumber":1433,"sourceCode":"      errors,\n    );\n  }\n}\n\nfunction finishConcurrentRender(\n  root: FiberRoot,\n  exitStatus: RootExitStatus,\n  finishedWork: Fiber,\n  lanes: Lanes,\n  renderEndTime: number, // Profiling-only\n) {\n  // TODO: The fact that most of these branches are identical suggests that some\n  // of the exit statuses are not best modeled as exit statuses and should be\n  // tracked orthogonally.\n  switch (exitStatus) {\n    case RootInProgress:\n    case RootFatalErrored: {\n      throw new Error('Root did not complete. This is a bug in React.');\n    }\n    case RootSuspendedWithDelay: {\n      if (!includesOnlyTransitions(lanes) && !includesOnlyRetries(lanes)) {\n        // Commit the placeholder.\n        break;\n      }\n    }\n    // Fallthrough\n    case RootSuspendedAtTheShell: {\n      // This is a transition, so we should exit without committing a\n      // placeholder and without scheduling a timeout. Delay indefinitely\n      // until we receive more data.\n      if (enableProfilerTimer && enableComponentPerformanceTrack) {\n        setCurrentTrackFromLanes(lanes);\n        logSuspendedRenderPhase(\n          renderStartTime,\n          renderEndTime,\n          lanes,","sourceCodeStart":1415,"sourceCodeEnd":1451,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-reconciler/src/ReactFiberWorkLoop.js#L1415-L1451","documentation":"finishConcurrentRender only accepts terminal exit statuses (the suspended variants, RootErrored, RootCompleted). Receiving RootInProgress or RootFatalErrored means the render pass ended without a usable finished tree — an invariant violation inside the reconciler (error code 345), not an error in application code. RootInProgress here implies the work loop exited while still marked in-progress; RootFatalErrored should have been handled before commit.","triggerScenarios":"The work loop returns with the root status still RootInProgress, or a fatally errored root is handed to the commit path — states that renderRootSync/finishConcurrentRender bookkeeping should have converted earlier.","commonSituations":"Canary/experimental regressions; races between sync and concurrent updates on the same root (several such bugs were fixed historically); third-party instrumentation or renderer forks interfering with exit-status bookkeeping.","solutions":["Reproduce on the latest stable React release and upgrade — many 'Root did not complete' reports are fixed regressions","Remove third-party renderers, devtools hooks, or monkey-patches to isolate the cause","Reduce to a minimal repro (often an interruption + update race) and file a React issue with the version and stack"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// wrap the app so render invariants are reported with version context\ncomponentDidCatch(error, info) {\n  if (/Root did not complete/.test(error.message)) {\n    report({kind: 'react-invariant', error, info, version: React.version});\n  }\n}","preventionTips":["Run the latest stable React — exit-status invariants have had multiple fixed regressions","Remove renderer forks and reconciler monkey-patches before debugging further","Keep a minimal repro ready when filing the upstream issue"],"tags":["internal-invariant","render","commit","concurrent"],"backgroundTag":"react-internal-invariant","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}