Repository navigation
Dev reload crashes with SurfaceHandler::setUIManager assert (link_.status != Status::Running) on RN 0.82.1 + RNN 8.6.0 #54845
Description
Description
When the app is using React Native Navigation and calls Navigation.setRoot, triggering a fast dev reload (Cmd + R in Metro) causes iOS to abort inside SurfaceHandler::setUIManager. The native code asserts that a surface must not be running when swapping the UIManager. During a reload the JS side tears down/creates roots rapidly, so a new setRoot arrives while the previous surface is still Running, which makes the assertion fire and the simulator crashes. Stopping the surface before swapping the UIManager (or queueing setRoot calls) prevents the crash, but the stock RN binary fails every time.
Steps to reproduce
- Start a RN 0.82.1 app that uses React Native Navigation (or anything calling Navigation.setRoot) with New Architecture + Hermes enabled.
- Launch the iOS simulator (tested on iPhone 17 Pro / iOS 26.1).
- After the root renders, trigger a dev reload (Cmd+R).
- The app immediately terminates with an assertion from SurfaceHandler.cpp (line 406).
Expected
Dev reload should re-create the surface cleanly without crashing.
Actual
App aborts with react_native_assert failure: link_.status != Status::Running && "Surface must not be running." coming from SurfaceHandler::setUIManager.
React Native Version
0.82.1
Affected Platforms
Runtime - iOS
Output of npx @react-native-community/cli info
System:
OS: macOS 26.1
CPU: Unknown
Memory: 109.25 MB / 32.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.18.0
path: /opt/homebrew/bin/node
Yarn:
version: 1.22.22
path: /opt/homebrew/bin/yarn
npm:
version: 10.9.3
path: /opt/homebrew/bin/npm
Watchman:
version: 2024.12.02.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.16.2
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 25.1
- iOS 26.1
- macOS 26.1
- tvOS 26.1
- visionOS 26.1
- watchOS 26.1
Android SDK:
API Levels:
- "28"
- "29"
- "30"
- "32"
- "33"
- "34"
Build Tools:
- 30.0.2
- 30.0.3
- 32.0.0
- 33.0.0
- 34.0.0
System Images:
- android-34 | Google Play ARM 64 v8a
Android NDK: Not Found
IDEs:
Android Studio: 2022.3 AI-223.8836.35.2231.10671973
Xcode:
version: 26.1.1/17B100
path: /usr/bin/xcodebuild
Languages:
Java:
version: 15.0.2
path: /usr/bin/javac
Ruby:
version: 2.6.10
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli":
installed: 20.0.0
wanted: 20.0.0
react:
installed: 19.1.1
wanted: 19.1.1
react-native:
installed: 0.82.1
wanted: 0.82.1
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: true
Stacktrace or Logs
E1210 18:44:06.062747 1832955904 SurfaceHandler.cpp:406] react_native_assert failure: link_.status != Status::Running && "Surface must not be running."
E1210 18:44:06.062762 1835249664 SurfaceHandler.cpp:406] react_native_assert failure: link_.status != Status::Running && "Surface must not be running."
Assertion failed: (link_.status != Status::Running && "Surface must not be running."), function setUIManager, file SurfaceHandler.cpp, line 406.
CoreSimulator 1051.9.4 - Device: iPhone 17 Pro (F4B4A94F-561C-4EF2-B686-DF06236B295A) - Runtime: iOS 26.1 (23B86)
MANDATORY Reproducer
Screenshots and Videos
No response
Activity
react-native-bot commentedon Dec 10, 2025
Warning
Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:
You can read more about about it on our website: How to report a bug.
migueldaipre commentedon Dec 15, 2025
Hey @solonifer, please, a basic reproducer is necessary for us to help.
It seems to me that your issue is not directly related to React Native Core.
Please add a minimal basic reproducer that simulates the issue without third-party libraries.
react-native-bot commentedon Dec 15, 2025
Caution
Invalid issue: This issue is not valid, either is not a bug in React Native, it doesn't match any of the issue template, or we can't help further with this.
reeywhaar commentedon Sep 22, 2026
Hitting this too, on a plain dev reload — worth saying it isn't specific to Fast Refresh, a Cmd+R does it just as reliably.
Environment
The surface is deallocated on a background queue while it is still
Running. What I think happens:RCTHost::_reloadWithShouldRestartSurfaces:restarts every attached surface, capturing it strongly:-[RCTSurfaceHostingView dealloc]calls[_surface stop]— which returns immediately, because the status is notRunningyet.[surface start]runs, and itsdispatch_async(QOS_CLASS_USER_INTERACTIVE)block (also a strongself) sets the status toRunning.dealloc→unregisterSurface→ assert.The reload path never got the same treatment.
What helped me. We consume the prebuilt React core, so patch-package cannot reach
RCTHost.mm— I swizzled the reload to skip the restart instead. With react-native-navigation the reloaded bundle callssetRootand builds fresh surfaces anyway, so the restart had nothing to do:Reloads are crash free at least, though still, the whole stack reloads.
What I think the actual fix is — untested here for the reason above, but it is the same change #48213 made to
createSurface:for (RCTFabricSurface *surface in [self _getAttachedSurfaces]) { [surface resetWithSurfacePresenter:self.surfacePresenter]; if (shouldRestartSurfaces) { - [_instance callFunctionOnBufferedRuntimeExecutor:[surface](facebook::jsi::Runtime &_) { [surface start]; }]; + __weak RCTFabricSurface *weakSurface = surface; + [_instance callFunctionOnBufferedRuntimeExecutor:[weakSurface](facebook::jsi::Runtime &_) { [weakSurface start]; }]; } }One dead end: holding the surface in
-[RNNReactView dealloc]past the restart does nothing.RCTHost's lambda owns it until the bundle loads, and that is not a delay you can outlast.