{"record":{"id":"32b96d60fa04c090","repo":"facebook/react","slug":"349-32b96d","errorCode":"349","errorMessage":"Expected a work-in-progress root. This is a bug in React. Please file an issue.","messagePattern":"Expected a work-in-progress root\\. This is a bug in React\\. Please file an issue\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-reconciler/src/ReactFiberHooks.js","lineNumber":1409,"sourceCode":"        ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)\n        : !isSubsetOfLanes(renderLanes, updateLane);\n\n      if (enableGestureTransition && updateLane === GestureLane) {\n        // This is a gesture optimistic update. It should only be considered as part of the\n        // rendered state while rendering the gesture lane and if the rendering the associated\n        // ScheduledGesture.\n        const scheduledGesture = update.gesture;\n        if (scheduledGesture !== null) {\n          if (scheduledGesture.count === 0 && !scheduledGesture.committing) {\n            // This gesture has already been cancelled. We can clean up this update.\n            update = update.next;\n            continue;\n          } else if (!isGestureRender(renderLanes)) {\n            shouldSkipUpdate = true;\n          } else {\n            const root: FiberRoot | null = getWorkInProgressRoot();\n            if (root === null) {\n              throw new Error(\n                'Expected a work-in-progress root. This is a bug in React. Please file an issue.',\n              );\n            }\n            // We assume that the currently rendering gesture is the one first in the queue.\n            shouldSkipUpdate = root.pendingGestures !== scheduledGesture;\n          }\n        }\n      }\n\n      if (shouldSkipUpdate) {\n        // Priority is insufficient. Skip this update. If this is the first\n        // skipped update, the previous update/state is the new base\n        // update/state.\n        const clone: Update<S, A> = {\n          lane: updateLane,\n          revertLane: update.revertLane,\n          gesture: update.gesture,\n          action: update.action,","sourceCodeStart":1391,"sourceCodeEnd":1427,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-reconciler/src/ReactFiberHooks.js#L1391-L1427","documentation":"Internal invariant inside updateReducer's queue processing. When a hook update carries the GestureLane (optimistic state scheduled by the experimental startGestureTransition API) and the current render is itself a gesture render, React looks up the work-in-progress FiberRoot to compare root.pendingGestures with the update's ScheduledGesture. If getWorkInProgressRoot() returns null at that point the reconciler's state is inconsistent; the message explicitly says this is a bug in React, not in application code.","triggerScenarios":"React is replaying a reducer queue that contains a gesture optimistic update (update.lane === GestureLane with a non-null update.gesture), isGestureRender(renderLanes) is true, and getWorkInProgressRoot() returns null while deciding whether to skip the update. Only reachable on builds where the enableGestureTransition flag is on and startGestureTransition was used.","commonSituations":"Using canary/experimental React builds or this rust-research branch with unstable_startGestureTransition; mixing gesture transitions with hydration, offscreen/Activity trees, or interrupted renders; skew between react and react-dom versions. Apps that never call startGestureTransition cannot hit this path.","solutions":["File an issue at https://github.com/facebook/react/issues with a minimal repro using startGestureTransition (the message requests it)","Pin the last canary/experimental version that worked, then retest on a newer release where the bug may be fixed","As a workaround, replace startGestureTransition with startTransition or an action (drops the gesture code path entirely)","Verify react and react-dom resolve to the exact same version (no duplicated or mismatched copies)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"class GestureErrorBoundary extends React.Component {\n  state = {error: null};\n  static getDerivedStateFromError(error) {\n    return {error};\n  }\n  componentDidCatch(error) {\n    reportToIssueTracker(error); // include React version + startGestureTransition repro\n  }\n  render() {\n    return this.state.error ? <StaticFallback /> : this.props.children;\n  }\n}","preventionTips":["Do not ship experimental gesture APIs (unstable_startGestureTransition) to production — pin to stable public APIs","Keep react and react-dom on the exact same version in the lockfile","Wrap experimental-feature subtrees in error boundaries so internal invariants degrade to fallback UI","Track canary changelogs for gesture-transition fixes and retest on upgrades"],"tags":["react","hooks","invariant","gesture-transition","use-optimistic","internal-bug"],"backgroundTag":"library-invariant-violation","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}