Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[iOS] Crash NSGenericException in FlutterPluginAppLifeCycleDelegate (mutation while enumeration) #182361

@eMxPi

Description

@eMxPi

Steps to reproduce

The crash occurs in FlutterPluginAppLifeCycleDelegate when the _delegates collection is modified (a delegate is added or removed) while it is being enumerated during a lifecycle event.

This typically happens if a plugin registers or unregisters a delegate inside a callback like application:didFinishLaunchingWithOptions:.

Steps to reproduce logic:

  1. A Flutter app starts on iOS.
  2. FlutterPluginAppLifeCycleDelegate begins iterating over _delegates to forward a lifecycle event.
  3. Inside the loop, one of the delegates (or a side effect) calls addDelegate: or removeDelegate:.
  4. The _delegates NSHashTable is mutated.
  5. The loop continues and crashes with NSGenericException because the collection was mutated during enumeration.

this crash has been introduced with 3.38.x and this PR : https://github.com/flutter/flutter/pull/176580/changes
On line 178,

for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) { should be replaced by for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in [_delegates allObjects]) {

like the original code.

I'm opening a PR with Tests accordingly and this should be fixed in a 3.38.X (we're facing more than 200k crashes everyday)

Expected results

The app should handle delegate registration/deregistration gracefully during lifecycle events without crashing.

Actual results

The app crashes with the following exception:

Fatal Exception: NSGenericException
Collection <NSHashTable: ...> was mutated while being enumerated.
Stack trace:
          Fatal Exception: NSGenericException
0  CoreFoundation                 0xc5964 __exceptionPreprocess
1  libobjc.A.dylib                0x31814 objc_exception_throw
2  CoreFoundation                 0x14e5dc +[__NSFastEnumerationEnumerator allocWithZone:]
3  Flutter                        0x3d66c -[FlutterPluginAppLifeCycleDelegate application:didFinishLaunchingWithOptions:isFallbackForScene:] + 178 (FlutterPluginAppLifeCycleDelegate.mm:178)
4  Flutter                        0x3d214 -[FlutterPluginAppLifeCycleDelegate application:didFinishLaunchingWithOptions:] + 151 (FlutterPluginAppLifeCycleDelegate.mm:151)
5  Flutter                        0x13090 -[FlutterAppDelegate application:didFinishLaunchingWithOptions:] + 54 (FlutterAppDelegate.mm:54)
6  Runner                         0xcae4 AppDelegate.application(_:didFinishLaunchingWithOptions:) + 79 (AppDelegate.swift:79)
7  Runner                         0xd2f0 @objc AppDelegate.application(_:didFinishLaunchingWithOptions:) (<compiler-generated>)
8  UIKitCore                      0x1420384 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]
9  UIKitCore                      0x1421928 -[UIApplication _callInitializationDelegatesWithActions:forScene:payload:fromOriginatingProcess:]
10 UIKitCore                      0x142594c -[UIApplication _runWithMainScene:transitionContext:completion:]
11 UIKitCore                      0xa2dce8 -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:]
12 UIKitCore                      0xd8eb0 _UIScenePerformActionsWithLifecycleActionMask
13 UIKitCore                      0xa2e564 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke
14 UIKitCore                      0xa2e0d8 -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:]
15 UIKitCore                      0xa2e37c -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]
16 UIKitCore                      0xa2de08 -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:]
17 UIKitCore                      0xa39288 __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke
18 UIKitCore                      0xeabec4 +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:fromCurrentState:actions:completion:]
19 UIKitCore                      0xd9cc8 _UISceneSettingsDiffActionPerformChangesWithTransitionContextAndCompletion
20 UIKitCore                      0xa38f84 -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]
21 UIKitCore                      0x8781e4 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.218
22 UIKitCore                      0x877278 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:]
23 UIKitCore                      0x877e50 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:]
24 UIKitCore                      0x1424918 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:]
25 UIKitCore                      0xed4cb4 -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:]
26 FrontBoardServices             0x1e3ec __95-[FBSScene _callOutQueue_didCreateWithTransitionContext:alternativeCreationCallout:completion:]_block_invoke
27 FrontBoardServices             0x1e8a4 -[FBSScene _callOutQueue_maybeCoalesceClientSettingsUpdates:]
28 FrontBoardServices             0x1e204 -[FBSScene _callOutQueue_didCreateWithTransitionContext:alternativeCreationCallout:completion:]
29 FrontBoardServices             0x8cbcc __93-[FBSWorkspaceScenesClient _callOutQueue_sendDidCreateForScene:transitionContext:completion:]_block_invoke.349
30 FrontBoardServices             0x2bf64 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
31 FrontBoardServices             0x48e04 -[FBSWorkspaceScenesClient _callOutQueue_sendDidCreateForScene:transitionContext:completion:]
32 FrontBoardServices             0x4a768 __92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke_2
33 FrontBoardServices             0x2bf64 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
34 libdispatch.dylib              0x1b7fc _dispatch_client_callout
35 libdispatch.dylib              0x6bb4 _dispatch_block_invoke_direct
36 BoardServices                  0x91d4 __BSSERVICEMAINRUNLOOPQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
37 BoardServices                  0x9054 BSServiceMainRunLoopSourceHandler
38 CoreFoundation                 0x68f10 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
39 CoreFoundation                 0x68e84 __CFRunLoopDoSource0
40 CoreFoundation                 0x46acc __CFRunLoopDoSources0
41 CoreFoundation                 0x1d6d8 __CFRunLoopRun
42 CoreFoundation                 0x1ca6c _CFRunLoopRunSpecificWithOptions
43 GraphicsServices               0x1498 GSEventRunModal
44 UIKitCore                      0x9ddf8 -[UIApplication _run]
45 UIKitCore                      0x46e54 UIApplicationMain
46 UIKitCore                      0x172938 -[UIScrollView contentInset]
47 Runner                         0xdcf0 main (AppDelegate.swift)
48 ???                            0x193592e28 (Manquant)

...

Code sample

This is an issue within the iOS Engine embedding (FlutterPluginAppLifeCycleDelegate). It cannot be reproduced with pure Dart code.

To reproduce the crash, a native plugin must add or remove a delegate while the FlutterPluginAppLifeCycleDelegate is iterating over its delegates (e.g., during didFinishLaunchingWithOptions).

Here is the Objective-C unit test logic that reproduces the NSGenericException:

// 1. Define a delegate that mutates the list during execution
@interface MutatingDelegate : NSObject <FlutterApplicationLifeCycleDelegate>
@property(nonatomic, weak) FlutterPluginAppLifeCycleDelegate* container;
@end

@implementation MutatingDelegate
- (BOOL)application:(UIApplication*)application
    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
    // THIS CAUSES THE CRASH: Modifying the collection while it is being iterated
    [self.container addDelegate:[[NSObject alloc] init]]; 
    return YES;
}
@end

// 2. The Test Case
- (void)testCrashReproduction {
    FlutterPluginAppLifeCycleDelegate* delegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
    MutatingDelegate* mutatingDelegate = [[MutatingDelegate alloc] init];
    mutatingDelegate.container = delegate;
    
    [delegate addDelegate:mutatingDelegate];
    
    // This line throws "Fatal Exception: NSGenericException"
    // "Collection <NSHashTable> was mutated while being enumerated."
    [delegate application:[UIApplication sharedApplication]
        didFinishLaunchingWithOptions:nil];
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
          Fatal Exception: NSGenericException
0  CoreFoundation                 0xc5964 __exceptionPreprocess
1  libobjc.A.dylib                0x31814 objc_exception_throw
2  CoreFoundation                 0x14e5dc +[__NSFastEnumerationEnumerator allocWithZone:]
3  Flutter                        0x3d66c -[FlutterPluginAppLifeCycleDelegate application:didFinishLaunchingWithOptions:isFallbackForScene:] + 178 (FlutterPluginAppLifeCycleDelegate.mm:178)
4  Flutter                        0x3d214 -[FlutterPluginAppLifeCycleDelegate application:didFinishLaunchingWithOptions:] + 151 (FlutterPluginAppLifeCycleDelegate.mm:151)
5  Flutter                        0x13090 -[FlutterAppDelegate application:didFinishLaunchingWithOptions:] + 54 (FlutterAppDelegate.mm:54)
6  Runner                         0xcae4 AppDelegate.application(_:didFinishLaunchingWithOptions:) + 79 (AppDelegate.swift:79)
7  Runner                         0xd2f0 @objc AppDelegate.application(_:didFinishLaunchingWithOptions:) (<compiler-generated>)
8  UIKitCore                      0x1420384 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]
9  UIKitCore                      0x1421928 -[UIApplication _callInitializationDelegatesWithActions:forScene:payload:fromOriginatingProcess:]
10 UIKitCore                      0x142594c -[UIApplication _runWithMainScene:transitionContext:completion:]
11 UIKitCore                      0xa2dce8 -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:]
12 UIKitCore                      0xd8eb0 _UIScenePerformActionsWithLifecycleActionMask
13 UIKitCore                      0xa2e564 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke
14 UIKitCore                      0xa2e0d8 -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:]
15 UIKitCore                      0xa2e37c -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]
16 UIKitCore                      0xa2de08 -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:]
17 UIKitCore                      0xa39288 __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke
18 UIKitCore                      0xeabec4 +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:fromCurrentState:actions:completion:]
19 UIKitCore                      0xd9cc8 _UISceneSettingsDiffActionPerformChangesWithTransitionContextAndCompletion
20 UIKitCore                      0xa38f84 -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]
21 UIKitCore                      0x8781e4 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.218
22 UIKitCore                      0x877278 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:]
23 UIKitCore                      0x877e50 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:]
24 UIKitCore                      0x1424918 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:]
25 UIKitCore                      0xed4cb4 -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:]
26 FrontBoardServices             0x1e3ec __95-[FBSScene _callOutQueue_didCreateWithTransitionContext:alternativeCreationCallout:completion:]_block_invoke
27 FrontBoardServices             0x1e8a4 -[FBSScene _callOutQueue_maybeCoalesceClientSettingsUpdates:]
28 FrontBoardServices             0x1e204 -[FBSScene _callOutQueue_didCreateWithTransitionContext:alternativeCreationCallout:completion:]
29 FrontBoardServices             0x8cbcc __93-[FBSWorkspaceScenesClient _callOutQueue_sendDidCreateForScene:transitionContext:completion:]_block_invoke.349
30 FrontBoardServices             0x2bf64 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
31 FrontBoardServices             0x48e04 -[FBSWorkspaceScenesClient _callOutQueue_sendDidCreateForScene:transitionContext:completion:]
32 FrontBoardServices             0x4a768 __92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke_2
33 FrontBoardServices             0x2bf64 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
34 libdispatch.dylib              0x1b7fc _dispatch_client_callout
35 libdispatch.dylib              0x6bb4 _dispatch_block_invoke_direct
36 BoardServices                  0x91d4 __BSSERVICEMAINRUNLOOPQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
37 BoardServices                  0x9054 BSServiceMainRunLoopSourceHandler
38 CoreFoundation                 0x68f10 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
39 CoreFoundation                 0x68e84 __CFRunLoopDoSource0
40 CoreFoundation                 0x46acc __CFRunLoopDoSources0
41 CoreFoundation                 0x1d6d8 __CFRunLoopRun
42 CoreFoundation                 0x1ca6c _CFRunLoopRunSpecificWithOptions
43 GraphicsServices               0x1498 GSEventRunModal
44 UIKitCore                      0x9ddf8 -[UIApplication _run]
45 UIKitCore                      0x46e54 UIApplicationMain
46 UIKitCore                      0x172938 -[UIScrollView contentInset]
47 Runner                         0xdcf0 main (AppDelegate.swift)
48 ???                            0x193592e28 (Manquant)
        

Flutter Doctor output

Doctor output
Waiting for another flutter command to release the startup lock...
[✓] Flutter (Channel stable, 3.38.7, on macOS 26.2 25C56 darwin-arm64, locale fr-FR) [1 159ms]
    • Flutter version 3.38.7 on channel stable at /Users/maxime_pontoire/fvm/versions/3.38.7
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 3b62efc2a3 (4 weeks ago), 2026-01-13 13:47:42 -0800
    • Engine revision 78fc3012e4
    • Dart version 3.10.7
    • DevTools version 2.51.1
    • Pub download mirror https://artifact.socrate.vsct.fr/artifactory/api/pub/all-pub
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop,
      enable-android, enable-ios, cli-animations, enable-native-assets, omit-legacy-version-file,
      enable-lldb-debugging

[✗] Android toolchain - develop for Android devices [1 105ms]
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/to/macos-android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.


[✓] Xcode - develop for iOS and macOS (Xcode 26.2) [2,3s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17C52
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [13ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Connected device (4 available) [6,5s]
    • eMxPi’s iPhone (wireless) (mobile) • 00008130-00063DDA2E04001C            • ios            • iOS
      26.3 23D127
    • iPhone + Watch (mobile)            • A2B07733-85AB-4812-BFD2-1822DB74E7CF • ios            •
      com.apple.CoreSimulator.SimRuntime.iOS-26-2 (simulator)
    • macOS (desktop)                    • macos                                • darwin-arm64   •
      macOS 26.2 25C56 darwin-arm64
    • Chrome (web)                       • chrome                               • web-javascript •
      Google Chrome 145.0.7632.46

[✓] Network resources [326ms]
    • All expected network resources are available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority issues at the top of the work listc: regressionIt was better in the past than it is nowengineflutter/engine related. See also e: labels.found in release: 3.38Found to occur in 3.38platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions