facebook/react · error · Error

Cannot requestFormReset() inside a startGestureTransition. T

Error message

Cannot requestFormReset() inside a startGestureTransition. There should be no side-effects associated with starting a Gesture until its Action is invoked. Move side-effects to the Action instead.

What it means

requestFormReset is the internal behind resetting a React-controlled form. Inside a gesture transition the current transition object carries gesture, and the scope callback of startGestureTransition runs in that context. React forbids resetting a form there: starting a Gesture must be free of side effects — DOM state changes belong to the Action the gesture eventually invokes, not to starting it.

Source

Thrown at packages/react-reconciler/src/ReactFiberHooks.js:3397

  if (transition === null) {
    if (__DEV__) {
      // An optimistic update occurred, but startTransition is not on the stack.
      // The form reset will be scheduled at default (sync) priority, which
      // is probably not what the user intended. Most likely because the
      // requestFormReset call happened after an `await`.
      // TODO: Theoretically, requestFormReset is still useful even for
      // non-transition updates because it allows you to update defaultValue
      // synchronously and then wait to reset until after the update commits.
      // I've chosen to warn anyway because it's more likely the `await` mistake
      // described above. But arguably we shouldn't.
      console.error(
        'requestFormReset was called outside a transition or action. To ' +
          'fix, move to an action, or wrap with startTransition.',
      );
    }
  } else if (enableGestureTransition && transition.gesture) {
    throw new Error(
      'Cannot requestFormReset() inside a startGestureTransition. ' +
        'There should be no side-effects associated with starting a ' +
        'Gesture until its Action is invoked. Move side-effects to the ' +
        'Action instead.',
    );
  }

  let stateHook: Hook = ensureFormComponentIsStateful(formFiber);
  const newResetState = {};
  if (stateHook.next === null) {
    // Hack alert. If formFiber is the workInProgress Fiber then
    // we might get a broken intermediate state. Try the alternate
    // instead.
    // TODO: We should really stash the Queue somewhere stateful
    // just like how setState binds the Queue.
    stateHook = (formFiber.alternate as any).memoizedState;
  }
  const resetStateHook: Hook = stateHook.next as any;

View on GitHub (pinned to eafeac097b)

Solutions

  1. Move the requestFormReset/form.reset() call into the Action the gesture invokes (run it when the action executes, not when the gesture starts)
  2. If the reset must precede the gesture, use startTransition or an action instead of a gesture transition
  3. Keep the startGestureTransition scope callback side-effect free (schedule reads, updates, and DOM mutations elsewhere)

Example fix

// before
startGestureTransition(timeline, () => {
  formRef.current.reset(); // throws: side effect at Gesture start
});

// after — run the reset inside the Action the Gesture invokes
startGestureTransition(timeline, () => {
  scheduleAction(() => {
    formRef.current.reset(); // side effects allowed inside the Action
  });
});
Defensive patterns

Strategy: validation

Validate before calling

// Keep gesture scope callbacks pure: assert no DOM side effects sneak in (dev)
function pureScope(fn: () => void): () => void {
  return () => {
    if (document.activeElement === null) { /* heuristic */ }
    fn();
  };
}

Try / catch

// The throw is routed through reportGlobalError by startGestureTransition, so catch it at the root:
createRoot(container, {
  onUncaughtError(error) {
    if (String(error).includes('requestFormReset')) {
      telemetry.count('form-reset-inside-gesture');
    }
  },
}).render(<App />);

Prevention

When it happens

Trigger: Calling requestFormReset (directly or via formRef.current.reset() on a React-controlled form) inside the synchronous scope callback passed to unstable_startGestureTransition / startGestureTransition(timeline, () => { ... }). The throw happens while transition.gesture is set.

Common situations: Porting startTransition habits (where synchronous side effects in the callback are tolerated) to the experimental gesture API; optimistically clearing inputs the moment a gesture begins; exploratory work on experimental gesture/timeline builds.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/e5616dd03dd49d11. Report an issue: GitHub.