You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After calling revalidatePath(), the browser receives the correct Flight data with the updated counter value, but the UI remains stuck showing the old value. This only happens:
In production mode
With short async delays (~5ms)
When the component is wrapped in Suspense
next-issue.mov
The issue was introduced in commit that upgraded React from eaee5308-20250728 to 9be531cd-20250729.
Root cause
The new resolveLazy function in ReactChildFiber.js catches promises and throws SuspenseException:
exportfunctionresolveLazy<T>(lazyType: LazyComponentType<T,any>): T{try{if(__DEV__){returncallLazyInitInDEV(lazyType);}constpayload=lazyType._payload;constinit=lazyType._init;returninit(payload);}catch(x){if(x!==null&&typeofx==='object'&&typeofx.then==='function'){// This lazy Suspended. Treat this as if we called use() to unwrap it.suspendedThenable=x;if(__DEV__){needsToResetSuspendedThenableDEV=true;}throwSuspenseException;}throwx;}}
When a lazy component's init() throws a short-lived promise (~5ms), this creates a race condition:
resolveLazy catches the promise and stores it in suspendedThenable
It throws SuspenseException to signal suspension
The promise resolves before React has finished setting up its subscription
React remains suspended, waiting for a signal that already passed
Steps To Reproduce
Link to code example: vercel/next.js#87529
The current behavior
After calling revalidatePath(), the browser receives the correct Flight data with the updated counter value, but the UI remains stuck showing the old value. This only happens:
next-issue.mov
The issue was introduced in commit that upgraded React from
eaee5308-20250728to9be531cd-20250729.Root cause
The new resolveLazy function in ReactChildFiber.js catches promises and throws SuspenseException:
When a lazy component's init() throws a short-lived promise (~5ms), this creates a race condition:
Previous working version
Without the try-catch, promises propagate naturally through React's existing Suspense machinery, which handles them correctly.
The expected behavior
After revalidatePath() is called: