Repository navigation
cupertino_http 2.4.0 crashes on iOS on wrapListenerBlock #1894
Copy link
Copy link
Closed
dangthaison91/http-dart
#1Labels
package:cupertino_httpIssues related to package:cupertino_httpIssues related to package:cupertino_httptype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)Incorrect behavior (everything from a crash to more subtle misbehavior)
Description
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
- 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
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
package:cupertino_httpIssues related to package:cupertino_httpIssues related to package:cupertino_httptype-bugIncorrect behavior (everything from a crash to more subtle misbehavior)Incorrect behavior (everything from a crash to more subtle misbehavior)
Activity
[-]cupertino_http crashes on iOS[/-][+]cupertino_http crashes on iOS on wrapListenerBlock[/+][-]cupertino_http crashes on iOS on wrapListenerBlock[/-][+]cupertino_http 2.4.0 crashes on iOS on wrapListenerBlock[/+]mraleph commentedon Mar 25, 2026
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
http/pkgs/cupertino_http/lib/src/cupertino_api.dart
Lines 1119 to 1121 in 8660fa7
cancelAllOperationsandwaitUntilAllOperationsAreFinishedto avoid any sort of races between isolate going away and callbacks firing from the queue.orestesgaolin commentedon Mar 25, 2026
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.
brianquinlan commentedon Mar 25, 2026
@liamappelbe
Is there a way that
cupertino_httpcould guard against this in general? For example:Where could
cupertino_httpinject logic to preventonCompletefrom 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 whenURLSessionis collected, that invalidates the wrappedNSURLSession?For
CupertinoClient, I can change theclosemethod like:@override void close() { - _urlSession?.finishTasksAndInvalidate(); + _urlSession?.invalidateAndCancel(); _urlSession = null; }But that will still lead to a crash if the developer doesn't call
closeor in the hot restart case described in flutter/flutter#182996brianquinlan commentedon Mar 25, 2026
I wonder if there is some way for the Objective-C trampolines generated by
package:ffigento avoid calling into Dart code if it has already been deleted? Or could we make the VM print a warning message rather thanFATALif the callback is not live (or is that not possible because callbacks can have return values)?mraleph commentedon Mar 25, 2026
The general answer to this is no. The only way to implement that reliably is to leak them.
brianquinlan commentedon Mar 25, 2026
I was able to reproduce the same issue using
CupertinoClient: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
onCompletehandler is run, and then, whencloseis called, it waits until the list of running tasks is empty. The problem is that I had to changecloseto returnFuture, which I can't do becausecloseis defined as part of theClientinterface and it returnsvoid.Even with the above, the crash can still occur if:
closeliamappelbe commentedon Mar 26, 2026
Might be able to fix this on the FFIgen side: dart-lang/native#3265. Main unknown is whether I can use
Dart_SendPortGetIdto detect if the target isolate is shut down.brianquinlan commentedon Mar 30, 2026
@liamappelbe had the clever suggestion of doing a fire-and-forgot of the cleanup code in the
Clientclosemethod.I thought that scheduled tasks were supposed to keep an isolate alive:
But that code prints 'here' and 'everywhere' and does not wait for the delayed closure to execute.
brianquinlan commentedon Mar 30, 2026
Before
dart:ffisupported asynchronous callbacks,package:cupertino_httphad its own callback implementation that usedSendPorts to communicate between Dart and native code (e.g.http/pkgs/cupertino_http/src/CUPHTTPClientDelegate.m
Line 58 in a41141d
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.
brianquinlan commentedon Mar 30, 2026
Ahhh.... one confusing thing is that
ObjCProtocolBuildinghas akeepIsolateAliveproperty, which is defaulttrue. But that won't keep isolates started withIsolate.run()alive because those isolates are terminated withexit.10 remaining items
brianquinlan commentedon Apr 27, 2026
I have another thought on how to approach this:
package:cupertino_httpcan keep track of active HTTP requests and throwStateErrorif there are any.package:cronet_httpalready does this because the underlyingcronetlibrary works like this;shutdownthrows if there are active requests.I'll also change the documentation to
closeto 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.clemortel commentedon Apr 28, 2026
We're hitting the same crash in production. Adding aggregate data in case it helps narrow it down.
Setup
Scale (rolling 7 days)
with iOS 26 rollout being the inflection point
Reproduction context
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
a workaround for a similar cupertino_http issue.
corepuncher commentedon May 31, 2026
I am seeing what appears to be the same crash in a production using
cupertino_http 2.4.0.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.
timmaffett commentedon Jun 4, 2026
Using
cupertino_http 3.0.2it 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) inwrapListenerBlocktrampoline onURLSession didReceiveResponse— iOS 26.5 / arm64e, release builds onlyWe'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_httpobjective_cffihttpException
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).Notes
didReceiveResponse), on a libdispatch worker thread, for ordinary requests — it is not tied to cancellation or timeouts.CupertinoClient.fromSessionConfiguration(ephemeral config +URLCache), never closed; requests are sent viapackage:http.IOClient(dart:io), i.e. don't instantiateCupertinoClient. This confirms the fault is in theobjective_ctrampoline path, not in our usage.PerLycke commentedon Jun 9, 2026
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 🤞
brianquinlan commentedon Jul 1, 2026
So you are not closing the
CupertinoClientbefore exiting the isolate? Could you try closing theCupertinoClientand see if you can reproduce the issue incupertino_http3.0.2.stwime commentedon Aug 13, 2026
I think ffigen 21.0.0 fixes this, and regenerating
cupertino_httpagainst 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-innative_cupertino_bindings.mstill calls the Dart callback directly (grep -c invokeListenerPortBlockreturns 0), so bumping objective_c on its own does nothing.I cloned the repo, changed only
ffigen: ^20.1.0to^21.0.0, and randart run ffigen --config ffigen.yaml. No config migration was needed, no hand-written source had to change, anddart analyze lib/is clean. YourIsolate.runrepro from above aborts at around iteration 9 on stock 3.0.2 and survives all 100 iterations after the regen.dart testgives 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 requestsand 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.
dcharkes commentedon Aug 13, 2026
cc @brianquinlan or otherwise @liamappelbe
liamappelbe commentedon Aug 13, 2026
@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?
[cupertino_http] Regenerate bindings with ffigen 21
[cupertino_http] Regenerate bindings with ffigen 21
[cupertino_http] Regenerate bindings with ffigen 21
[cupertino_http] Regenerate bindings with ffigen 21
stwime commentedon Aug 14, 2026
@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