Skip to content

cupertino_http 2.4.0 crashes on iOS on wrapListenerBlock #1894

Closed
dangthaison91/http-dart
#1

Description

@orestesgaolin

Typically user leaves the app and it crashes with the following stacktrace:

SIGABRT: ZTUM
  libsystem_kernel    0x1d79981dc  __pthread_kill
  libsystem_pthread   0x210fe3c18  pthread_kill
  libsystem_c         0x18eb23050  abort
  Flutter             0x10ae56c9c  dart::Assert::Fail (assert.cc:54)
  Flutter             0x10af104dc  DLRT_GetFfiCallbackMetadata (runtime_entry.cc:5144)
  0x114945d10  <redacted>
  cupertino_http      0x10982c34c  ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke (native_cupertino_bindings.m:534)
  cupertino_http      0x10982c34c  ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke (native_cupertino_bindings.m:534)
  cupertino_http      0x10982c758  _NativeCupertinoHttp_protocolTrampoline_xx612k (native_cupertino_bindings.m:555)
  CFNetwork           0x18809b2e8  __81-[__NSCFURLSessionDelegateWrapper dataTask:didReceiveResponse:completionHandler:]_block_invoke
  Foundation          0x18576543c  __NSINDEXSET_IS_CALLING_OUT_TO_A_BOOL_BLOCK__
  Foundation          0x18579be2c  -[NSBlockOperation main]
  Foundation          0x18579b570  __NSOPERATION_IS_INVOKING_MAIN__
  Foundation          0x18579b7f8  -[NSOperation start]
  Foundation          0x18579bbac  __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__
  Foundation          0x18579d1c8  __NSOQSchedule_f
  libdispatch         0x18ea772b4  _dispatch_block_async_invoke2
  libdispatch         0x18ea81580  _dispatch_client_callout
  libdispatch         0x18ea6c55c  _dispatch_continuation_pop
  libdispatch         0x18ea6bbd0  _dispatch_async_redirect_invoke
  libdispatch         0x18ea79dac  _dispatch_root_queue_drain
  libdispatch         0x18ea7a548  _dispatch_worker_thread2
  libsystem_pthread   0x210fdc9cc  _pthread_wqthread

  libsystem_kernel    0x1d79981dc  __pthread_kill
  libsystem_pthread   0x210fe3c18  pthread_kill
  libsystem_c         0x18eb23050  abort
  Flutter             0x10ae56c9c  dart::Assert::Fail (assert.cc:54)
  Flutter             0x10af104dc  DLRT_GetFfiCallbackMetadata (runtime_entry.cc:5144)
  0x114945d10  <redacted>
  cupertino_http      0x10982c34c  ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke (native_cupertino_bindings.m:534)
  cupertino_http      0x10982c34c  ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke (native_cupertino_bindings.m:534)
  cupertino_http      0x10982c758  _NativeCupertinoHttp_protocolTrampoline_xx612k (native_cupertino_bindings.m:555)
  CFNetwork           0x18809b2e8  __81-[__NSCFURLSessionDelegateWrapper dataTask:didReceiveResponse:completionHandler:]_block_invoke
  Foundation          0x18576543c  __NSINDEXSET_IS_CALLING_OUT_TO_A_BOOL_BLOCK__
  Foundation          0x18579be2c  -[NSBlockOperation main]
  Foundation          0x18579b570  __NSOPERATION_IS_INVOKING_MAIN__
  Foundation          0x18579b7f8  -[NSOperation start]
  Foundation          0x18579bbac  __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__
  Foundation          0x18579d1c8  __NSOQSchedule_f
  libdispatch         0x18ea772b4  _dispatch_block_async_invoke2
  libdispatch         0x18ea81580  _dispatch_client_callout
  libdispatch         0x18ea6c55c  _dispatch_continuation_pop
  libdispatch         0x18ea6bbd0  _dispatch_async_redirect_invoke
  libdispatch         0x18ea79dac  _dispatch_root_queue_drain
  libdispatch         0x18ea7a548  _dispatch_worker_thread2
  libsystem_pthread   0x210fdc9cc  _pthread_wqthread

flutter doctor

[✓] Flutter (Channel stable, 3.38.4, on macOS 15.6.1 24G90 darwin-arm64, locale pl-PL) [435ms]
    • Flutter version 3.38.4 on channel stable at /Users/dominik/fvm/versions/3.38.4
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 66dd93f9a2 (3 months ago), 2025-12-03 14:56:10 -0800
    • Engine revision a5cb96369e
    • Dart version 3.10.3
    • DevTools version 2.51.1
    • Feature flags: enable-web, no-enable-linux-desktop, enable-macos-desktop, no-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 (Android SDK version 36.1.0) [6,3s]
    • Android SDK at /Users/dominik/Library/Android/sdk
    • Emulator version 36.4.10.0 (build_id 15004761) (CL:N/A)
    ✗ cmdline-tools component is missing.
      Try installing or updating Android Studio.
      Alternatively, download the tools from https://developer.android.com/studio#command-line-tools-only and make sure to
      set the ANDROID_HOME environment variable.
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/macos-android-setup for more details.

[✓] Xcode - develop for iOS and macOS (Xcode 26.3) [1 370ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17C529
    • CocoaPods version 1.16.2

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

[✓] Connected device (4 available) [10,2s]
    • SM S911B (mobile)          • RFCW60DP85H               • android-arm64  • Android 16 (API 36)
    • iPhone (wireless) (mobile) • 00008101-0001510C3CF0001E • ios            • iOS 26.3.1 23D8133
    • macOS (desktop)            • macos                     • darwin-arm64   • macOS 15.6.1 24G90 darwin-arm64
    • Chrome (web)               • chrome                    • web-javascript • Google Chrome 146.0.7680.80

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

! Doctor found issues in 1 category.

Dependencies

deps.txt

  • issue occurs across wide range of iOS versions
  • issue seems to occur after moving the app to the background after successful HTTP requests - 99%+ issues occur when app is not in the foreground according to Sentry

Activity

  1. added
    type-bugIncorrect behavior (everything from a crash to more subtle misbehavior)
    on Mar 17, 2026
  2. changed the title [-]cupertino_http crashes on iOS[/-] [+]cupertino_http crashes on iOS on wrapListenerBlock[/+] on Mar 17, 2026
  3. changed the title [-]cupertino_http crashes on iOS on wrapListenerBlock[/-] [+]cupertino_http 2.4.0 crashes on iOS on wrapListenerBlock[/+] on Mar 17, 2026
  4. mraleph commented on Mar 25, 2026

    @mraleph
    Member

    I think it is quite likely the same issue that was reported flutter/flutter#182996 - callbacks still fire but underlying isolate went away.

    I would guess this is related to the code here

    final queue = ncb.NSOperationQueue()
    ..maxConcurrentOperationCount = 1
    ..name = 'cupertino_http.NSURLSessionDelegateQueue'.toNSString();
    which creates NSOperationQueue. When isolate goes away - we will release the queue, via native finalizer, but I think that might not be enough to stop the queue from running. You need to call cancelAllOperations and waitUntilAllOperationsAreFinished to avoid any sort of races between isolate going away and callbacks firing from the queue.

  5. orestesgaolin commented on Mar 25, 2026

    @orestesgaolin
    Author

    Thank you for linking this back. I'm happy to run a test with our beta users as soon as there's tentative/speculative fix.

  6. brianquinlan commented on Mar 25, 2026

    @brianquinlan
    Collaborator

    @liamappelbe

    Is there a way that cupertino_http could guard against this in general? For example:

    import 'dart:async';
    import 'dart:io';
    import 'dart:isolate';
    import 'package:cupertino_http/cupertino_http.dart';
    
    void main() async {
      final server = await HttpServer.bind('localhost', 0);
      server.listen((request) async {
        await Future.delayed(const Duration(milliseconds: 500));
        request.response.write('Hello');
        await request.response.close();
      });
    
      print('Server listening on ${server.port}');
      final uri = Uri.parse('http://localhost:${server.port}');
    
      for (int i = 0; i < 100; ++i) {
        print('Iteration $i');
        unawaited(Isolate.run(() {
          final config = URLSessionConfiguration.defaultSessionConfiguration();
          final session = URLSession.sessionWithConfiguration(config,
            // `onComplete` will be triggered after the Isolate exits
            onComplete: (s, t, e) => print('Should not see this')
          );
          final task = session.dataTaskWithRequest(URLRequest.fromUrl(uri));
          task.resume();
          // Isolate finishes and shuts down here, while task is in flight.
        }));
        await Future.delayed(const Duration(milliseconds: 50));
      }
    
      print('Waiting for native crashes...');
      await Future.delayed(const Duration(seconds: 5));
      await server.close();
    }
    ± dart run repro_with_classes.dart
    Running build hooks...
    Server listening on 57152
    Iteration 0
    Iteration 1
    Iteration 2
    Iteration 3
    Iteration 4
    Iteration 5
    Iteration 6
    Iteration 7
    Iteration 8
    Iteration 9
    Iteration 10
    Iteration 11
    Iteration 12
    ../../runtime/vm/runtime_entry.cc: 5122: error: Callback invoked after it has been deleted.
    version=3.11.2 (stable) (Tue Mar 10 01:02:11 2026 -0700) on "macos_arm64"
    pid=95365, thread=19971, isolate_group=(nil)(0x0), isolate=(nil)(0x0)
    os=macos, arch=arm64, comp=no, sim=no
    isolate_instructions=0, vm_instructions=10082ce20
    fp=170656a20, sp=1706569f0, pc=100ad0bd4
    ../../runtime/vm/runtime_entry.cc: 5122: error: Callback invoked after it has been deleted.
    Aborting reentrant request for stack trace.
    Abort trap: 6

    Where could cupertino_http inject logic to prevent onComplete from running? AFAIK, there is no way for Dart code to be informed when its Isolate is exiting (unless you control Isolate creation). Could I write some native code, that is triggered by a native finalizer when URLSession is collected, that invalidates the wrapped NSURLSession?

    For CupertinoClient, I can change the close method like:

      @override
      void close() {
    -    _urlSession?.finishTasksAndInvalidate();
    +    _urlSession?.invalidateAndCancel(); 
        _urlSession = null;
      }

    But that will still lead to a crash if the developer doesn't call close or in the hot restart case described in flutter/flutter#182996

  7. brianquinlan commented on Mar 25, 2026

    @brianquinlan
    Collaborator

    I wonder if there is some way for the Objective-C trampolines generated by package:ffigen to avoid calling into Dart code if it has already been deleted? Or could we make the VM print a warning message rather than FATAL if the callback is not live (or is that not possible because callbacks can have return values)?

  8. mraleph commented on Mar 25, 2026

    @mraleph
    Member

    I wonder if there is some way for the Objective-C trampolines generated by package:ffigen to avoid calling into Dart code if it has already been deleted?

    The general answer to this is no. The only way to implement that reliably is to leak them.

  9. brianquinlan commented on Mar 25, 2026

    @brianquinlan
    Collaborator

    I was able to reproduce the same issue using CupertinoClient:

    import 'dart:async';
    import 'dart:io';
    import 'dart:isolate';
    import 'package:cupertino_http/cupertino_http.dart';
    
    void main() async {
      final server = await HttpServer.bind('localhost', 0);
      server.listen((request) async {
        await Future.delayed(const Duration(milliseconds: 500));
        request.response.write('Hello');
        await request.response.close();
      });
    
      print('Server listening on ${server.port}');
      final uri = Uri.parse('http://localhost:${server.port}');
    
      for (int i = 0; i < 100; ++i) {
        print('Iteration $i');
        await Isolate.run(() async {
          final client = CupertinoClient.defaultSessionConfiguration();
          client.get(uri).ignore();
          client.close();
          // Isolate finishes and shuts down here, while task is in flight.
        });
        await Future.delayed(const Duration(milliseconds: 50));
      }
    
      print('Waiting for native crashes...');
      await Future.delayed(const Duration(seconds: 5));
      await server.close();
    }

    Running this results in the same stack trace as above. But fixing this is harder than I thought: even after calling [NSURLSession invalidateAndCancel], [NSOperationQueue cancelAllOperations] and [NSOperationQueue waitUntilAllOperationsAreFinished], the callback can still be invoked (maybe it is already inflight and waiting for the Dart isolate to be available for dispatch?).

    I was able to make the above code work by maintaining a global list of running tasks, having each task remove itself when its onComplete handler is run, and then, when close is called, it waits until the list of running tasks is empty. The problem is that I had to change close to return Future, which I can't do because close is defined as part of the Client interface and it returns void.

    Even with the above, the crash can still occur if:

    1. flutter reload is invoked
    2. the developer doesn't call close
  10. liamappelbe commented on Mar 26, 2026

    @liamappelbe
    Contributor

    Might be able to fix this on the FFIgen side: dart-lang/native#3265. Main unknown is whether I can use Dart_SendPortGetId to detect if the target isolate is shut down.

  11. brianquinlan commented on Mar 30, 2026

    @brianquinlan
    Collaborator

    @liamappelbe had the clever suggestion of doing a fire-and-forgot of the cleanup code in the Client close method.

    I thought that scheduled tasks were supposed to keep an isolate alive:

    void main() async {
      Isolate.run(() async {
        print('here');
        Future.delayed(const Duration(seconds: 10), () {
          print('there');
        });
        print('everywhere');
      });
    }

    But that code prints 'here' and 'everywhere' and does not wait for the delayed closure to execute.

  12. brianquinlan commented on Mar 30, 2026

    @brianquinlan
    Collaborator

    Before dart:ffi supported asynchronous callbacks, package:cupertino_http had its own callback implementation that used SendPorts to communicate between Dart and native code (e.g.

    - (void)URLSession:(NSURLSession *)session
    ).

    I could resurrect that code. Possibly it could also set a timeout for its locks and, if the lock times-out, it could most a message to Dart. If that post fails then it could assume that the receive port is closed and that the isolate is dead.

  13. brianquinlan commented on Mar 30, 2026

    @brianquinlan
    Collaborator

    Ahhh.... one confusing thing is that ObjCProtocolBuilding has a keepIsolateAlive property, which is default true. But that won't keep isolates started with Isolate.run() alive because those isolates are terminated with exit.

  14. 10 remaining items

  15. brianquinlan commented on Apr 27, 2026

    @brianquinlan
    Collaborator

    I have another thought on how to approach this: package:cupertino_http can keep track of active HTTP requests and throw StateError if there are any. package:cronet_http already does this because the underlying cronet library works like this; shutdown throws if there are active requests.

    I'll also change the documentation to close to be more emphatic that it should be called before isolate exit.

    This won't prevent a crash if the user doesn't close CupertinoClient (and, in fact, you may crash after the excepting is thrown) but it is better than nothing and at least you will understand the "why" of the crash.

  16. clemortel commented on Apr 28, 2026

    @clemortel

    We're hitting the same crash in production. Adding aggregate data in case it helps narrow it down.

    Setup

    • Flutter iOS app in production, cupertino_http 2.4.0 pulled transitively via native_dio_adapter 1.5.1
    • All Dio traffic on iOS goes through NativeAdapter(), no direct cupertino_http use

    Scale (rolling 7 days)

    • ~244 events / ~106 unique users
    • SIGNAL_REPETITIVE from Crashlytics — ~5 crashes per affected user
    • 3 distinct issue variants under one Crashlytics issue
    • First seen in our app v1.2.491, still present at v1.2.622 — bug has not been triggered by any app change in our codebase across that range, which lines up
      with iOS 26 rollout being the inflection point

    Reproduction context

    • 100% in processState: BACKGROUND (matches OP's "99%+ when not in foreground")
    • iOS 26.0, 26.3, 26.4 all confirmed
    • Devices: iPhone 13 Pro / 14 Pro / 15 Pro (ARM64)
    • Memory at crash: 4.4–7.3 GB resident, 178–222 MB free (high pressure but well below jetsam limits)
    • Last breadcrumb is consistently a screen_view followed shortly by analytics events — the user backgrounds the app while requests are in flight

    Stack signatures we see (three variants of the same root cause)

    Variant A — didReceiveData path:
    abort
    InternalFlutterGpu_Texture_AsImage ← misattributed, real symbol unresolved
    _NativeCupertinoHttp_protocolTrampoline_1tz5yf
    __77-[__NSCFURLSessionDelegateWrapper dataTask:didReceiveData:completionHandler:]_block_invoke_3
    -[NSBlockOperation main]

    Variant B — didReceiveResponse path:
    abort
    InternalFlutterGpu_Texture_AsImage
    ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke
    _NativeCupertinoHttp_protocolTrampoline_xx612k
    __81-[__NSCFURLSessionDelegateWrapper dataTask:didReceiveResponse:completionHandler:]_block_invoke

    Variant C — same path, deeper mangling into Swift symbol thunks.

    Note on symbolication: the InternalFlutterGpu_Texture_AsImage frame is misattributed (offsets 159 KB – 1.2 MB from the named export) — Crashlytics resolves
    to the nearest exported symbol in the stripped Flutter.framework binary. Per OP, the real crash is DLRT_GetFfiCallbackMetadata. App-side dSYM upload is
    configured correctly via flutterfire upload-crashlytics-symbols; the engine binary just doesn't ship debuggable symbols.

    Workarounds tried

  17. corepuncher commented on May 31, 2026

    @corepuncher

    I am seeing what appears to be the same crash in a production using cupertino_http 2.4.0.

    Exception Type: EXC_CRASH (SIGABRT)
    Triggered by Thread: 16
    
    Thread 16 Crashed:
    libsystem_kernel.dylib __pthread_kill
    libsystem_pthread.dylib pthread_kill
    libsystem_c.dylib abort
    Flutter dart::Assert::Fail(char const*, ...) const
    Flutter DLRT_GetFfiCallbackMetadata
    cupertino_http ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke
    cupertino_http _NativeCupertinoHttp_protocolTrampoline_xx612k
    CFNetwork __NSCFURLSessionDelegateWrapper dataTask:didReceiveResponse:completionHandler:
    Foundation -[NSBlockOperation main]
    

    The app uses CupertinoClient.fromSessionConfiguration(URLSessionConfiguration.defaultSessionConfiguration()) through a shared API client layer.

    Updated Info: In my app, one user reproduced the same crash family while the app was foregrounded, actively panning map/tiled imagery (so hundreds of tile requests). The app uses native_dio_adapter for FlutterMap tile imagery, which depends on cupertino_http, so many concurrent tile requests may be triggering the same wrapListenerBlock/protocolTrampoline path.

  18. timmaffett commented on Jun 4, 2026

    @timmaffett

    Using cupertino_http 3.0.2 it still happens - oddly when running locally on device in release mode it seems to work - but 3 consecutive TestFlight builds (with different workaround attempts to not crash) crashed immediately on device. Then I found this issue...

    Crash: EXC_BAD_ACCESS (null PC) in wrapListenerBlock trampoline on URLSession didReceiveResponse — iOS 26.5 / arm64e, release builds only

    We're hitting this reliably in release/TestFlight builds on iOS 26.5. It is 100% reproducible in release and never reproduces under the debugger (JIT) — consistent with an AOT / arm64e code-pointer issue in the generated Objective-C block trampoline.

    Environment

    cupertino_http 3.0.2
    objective_c 9.4.1
    ffi 2.2.0
    http 1.6.0
    Flutter 3.44.1 (stable)
    OS iOS 26.5 (23F77)
    Devices iPhone 16e (iPhone17,5) and iPhone 16 Pro Max (iPhone17,2) — both arm64e
    Build release (AOT); reproduced across 3 separate builds, identical signature

    Exception

    Exception Type:  EXC_BAD_ACCESS (SIGKILL/SIGBUS)
    Exception Subtype: KERN_PROTECTION_FAILURE at 0x0000000000000000
    Termination Reason: CODESIGNING 2 Invalid Page  (Instruction Abort / Translation fault, esr 0x82000006)
    Registers: pc = 0x0000000000000000   lr = 0x0000000112965a3c (into cupertino_http)
    

    The CPU branches to pc = 0x0 — the listener block's underlying callback pointer is null/invalid when CFNetwork invokes the delegate.

    Faulting thread (symbolicated)

    dSYM UUID 0CEA1CB2-26CE-35CC-AD84-AE661D904629 (matches the crash image).

    0   ???                       0x0000000000000000            <- branch to null PC
    1   cupertino_http            ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke + 204
    2   cupertino_http            _NativeCupertinoHttp_protocolTrampoline_xx612k + 144
    3   CFNetwork                 __81-[__NSCFURLSessionDelegateWrapper dataTask:didReceiveResponse:completionHandler:]_block_invoke + 168
    4   libdispatch.dylib         _dispatch_call_block_and_release + 32
    5   libdispatch.dylib         _dispatch_client_callout + 16
    6   libdispatch.dylib         _dispatch_lane_serial_drain + 740
    7   libdispatch.dylib         _dispatch_lane_invoke + 448
    8   libdispatch.dylib         _dispatch_root_queue_drain_deferred_wlh + 284
    9   libdispatch.dylib         _dispatch_workloop_worker_thread + 720
    10  libsystem_pthread.dylib   _pthread_wqthread + 292
    11  libsystem_pthread.dylib   start_wqthread + 8
    

    Notes

    • The crash fires on the first delegate callback (didReceiveResponse), on a libdispatch worker thread, for ordinary requests — it is not tied to cancellation or timeouts.
    • The client is a single long-lived CupertinoClient.fromSessionConfiguration (ephemeral config + URLCache), never closed; requests are sent via package:http.
    • Workaround that fully avoids it: switch the iOS HTTP client to IOClient (dart:io), i.e. don't instantiate CupertinoClient. This confirms the fault is in the objective_c trampoline path, not in our usage.
  19. PerLycke commented on Jun 9, 2026

    @PerLycke
    Crashed: cupertino_http.NSURLSessionDelegateQueue (QOS: UNSPECIFIED)
    0  libsystem_kernel.dylib         0x7200 __pthread_kill + 8
    1  libsystem_pthread.dylib        0x71ac pthread_kill + 268
    2  libsystem_c.dylib              0x20c8c abort + 180
    3  Flutter                        0x52114c InternalFlutterGpu_Texture_AsImage + 159476
    4  Flutter                        0x5d5118 InternalFlutterGpu_Texture_AsImage + 896704
    5  ???                            0x10c2653c8 (Missing)
    6  cupertino_http                 0x8250 ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke + 204
    7  cupertino_http                 0x8250 ___NativeCupertinoHttp_wrapListenerBlock_xx612k_block_invoke + 204
    8  cupertino_http                 0x8620 _NativeCupertinoHttp_protocolTrampoline_xx612k + 144
    9  CFNetwork                      0x6f934 CFURLConnectionCreateWithProperties + 11296
    10 Foundation                     0xa72f0 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 24
    11 Foundation                     0x7b4a0 -[NSBlockOperation main] + 104
    12 Foundation                     0x7b430 __NSOPERATION_IS_INVOKING_MAIN__ + 16
    13 Foundation                     0x3c7d8 -[NSOperation start] + 708
    14 Foundation                     0x3c50c __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 16
    15 Foundation                     0x41c20 __NSOQSchedule_f + 172
    16 libdispatch.dylib              0x13114 _dispatch_block_async_invoke2 + 148
    17 libdispatch.dylib              0x3fdc _dispatch_client_callout + 20
    18 libdispatch.dylib              0x746c _dispatch_continuation_pop + 504
    19 libdispatch.dylib              0x6ad4 _dispatch_async_redirect_invoke + 584
    20 libdispatch.dylib              0x15a6c _dispatch_root_queue_drain + 396
    21 libdispatch.dylib              0x16284 _dispatch_worker_thread2 + 164
    22 libsystem_pthread.dylib        0xdbc _pthread_wqthread + 228
    23 libsystem_pthread.dylib        0xb98 start_wqthread + 8
    

    This is what we see in Crashlytics, too often, related to this issue. I'm not invested enough in the code to perfectly understand the cause or contribute to a fix, but I hope others are 🤞

  20. brianquinlan commented on Jul 1, 2026

    @brianquinlan
    Collaborator
    • The client is a single long-lived CupertinoClient.fromSessionConfiguration (ephemeral config + URLCache), never closed; requests are sent via package:http.

    So you are not closing the CupertinoClient before exiting the isolate? Could you try closing the CupertinoClient and see if you can reproduce the issue in cupertino_http 3.0.2.

  21. stwime commented on Aug 13, 2026

    @stwime
    Contributor

    I think ffigen 21.0.0 fixes this, and regenerating cupertino_http against it turned out to be a one-line change.

    dart-lang/native#3265 closed on 2026-07-22 and shipped in ffigen 21.0.0 ("Fix memory leaks, deadlocks, and crashes that can happen in callbacks, if the target isolate is shut down"), with the runtime half in objective_c 9.5.0. But 3.0.2 pins ffigen: ^20.1.0, and its checked-in native_cupertino_bindings.m still calls the Dart callback directly (grep -c invokeListenerPortBlock returns 0), so bumping objective_c on its own does nothing.

    I cloned the repo, changed only ffigen: ^20.1.0 to ^21.0.0, and ran dart run ffigen --config ffigen.yaml. No config migration was needed, no hand-written source had to change, and dart analyze lib/ is clean. Your Isolate.run repro from above aborts at around iteration 9 on stock 3.0.2 and survives all 100 iterations after the regen. dart test gives 542 passed / 34 skipped both before and after, which is the part I'd most want a second opinion on, since the regen rewrites about 57k lines.

    On closing the client first: that can't help as of 3.0.2. Every iteration of the repro printed Bad state: cannot close with running requests and the process still aborted. close() throws in exactly the situation that crashes.

    Would you consider a release with regenerated bindings? Happy to open a PR.

  22. dcharkes commented on Aug 13, 2026

    @dcharkes
    Contributor

    Would you consider a release with regenerated bindings? Happy to open a PR.

    cc @brianquinlan or otherwise @liamappelbe

  23. liamappelbe commented on Aug 13, 2026

    @liamappelbe
    Contributor

    @stwime Sounds good. Brian is back from vacation next week. I'm sure he'd appreciate a PR. Would it make sense to turn the repro case into a regression test, or would it take too long to run because it's flaky and has to be repeated?

  24. stwime commented on Aug 14, 2026

    @stwime
    Contributor

    @liamappelbe test looks to be cheap and reliable, so I added one. On my machine, old bindings fail at iteration 9 every time I run it (15/15 runs, ~0.9s in), with regenerated bindings surviving 100. I set it to 20 iterations, which costs about 5s, maybe a little more on the CI.

    PR is at #1975

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

package:cupertino_httpIssues related to package:cupertino_httptype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions