Skip to content

[in_app_purchase] StoreKit2 error logs about using a platform channel from a native thread #166493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
edpizzi opened this issue Apr 2, 2025 · 2 comments · May be fixed by flutter/packages#9068
Labels
p: in_app_purchase Plugin for in-app purchase package flutter/packages repository. See also p: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team

Comments

@edpizzi
Copy link

edpizzi commented Apr 2, 2025

Steps to reproduce

  1. Clone flutter/packages repo (tested at commit 8f5844af8135a61becd731981c000b00814facc4)
  2. Configure packages/in_app_purchase/in_app_purchase/example app to work with StoreKit in XCode, so products appear in the iOS simulator in dev builds.
  3. Edit main() to enable StoreKit 2 (snippet below). Ensure that "Using StoreKit 2" is logged.
  4. Purchase the "upgrade", then click "restore purchases". If you don't see the log line, restart and retry. For me this is nondeterministic, but if you try this a few times you may see the error log. I also have better luck seeing the error if I run it from XCode directly.

Alternative path to reproduce:
4a. Launch from XCode. Purchase the "Upgrade", then refund it in XCode's Transactions log. Restart and repeat as necessary. The error is logged just after the refund is issued.

Expected results

(no error logs about thread safety)

Actual results

I get the same log line, whether triggered from restore purchases or on StoreKit Test refund:

[ERROR:flutter/shell/common/shell.cc(1053)] The 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.

This seems like it could be a severe error, so I'm reluctant to ignore it and launch with SK2 enabled unless maintainers are confident that this is not something that is likely to result in crashes. (I have not observed crashes, just this error message, but I'm testing on just one device.)

I observe the same error logs in a real iOS app, only when I enable SK2 (also tested using StoreKit Test). Reporting since SK1 is deprecated.

Code sample

Enabling storekit2 in the in_app_purchase example app
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (Platform.isIOS) {
    final bool sk2 = await InAppPurchaseStoreKitPlatform.enableStoreKit2();
    print("Using StoreKit ${sk2 ? "2" : "1"}");
  }

  runApp(_MyApp());
}

Screenshots or Video

No response

Logs

Logs
flutter: The Dart VM service is listening on http://127.0.0.1:53431/Jhql3btwDU8=/
flutter: Using StoreKit 2
[ERROR:flutter/shell/common/shell.cc(1053)] The 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.27.1, on macOS 14.6 23G80 darwin-arm64, locale en-US)
    • Flutter version 3.27.1 on channel stable at /Users/edpizzi/src/libraries/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 17025dd882 (4 months ago), 2024-12-17 03:23:09 +0900
    • Engine revision cb4b5fff73
    • Dart version 3.6.0
    • DevTools version 2.40.2

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/edpizzi/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16C5032a
    • CocoaPods version 1.16.2

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

[✓] Android Studio (version 2024.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)

[✓] IntelliJ IDEA Community Edition (version 2024.3.1.1)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 83.0.4
    • Dart plugin version 243.23177

[✓] VS Code (version 1.96.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.102.0

[✓] Connected device (6 available)
(edit: I'm redacting some of these devices)
    • iPad Air 11-inch (M2) (mobile)     • 2BE6F5D4-F9BA-463B-B366-901B4974DB83 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-18-2 (simulator)
    • iPad Air (5th generation) (mobile) • 0361A570-AFD6-441D-BBCE-818BB9F63095 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-17-4 (simulator)
    • macOS (desktop)                    • macos                                • darwin-arm64   • macOS 14.6 23G80 darwin-arm64
    • Mac Designed for iPad (desktop)    • mac-designed-for-ipad                • darwin         • macOS 14.6 23G80 darwin-arm64
    • Chrome (web)                       • chrome                               • web-javascript • Google Chrome 134.0.6998.166

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

• No issues found!
@darshankawar darshankawar added the in triage Presently being triaged by the triage team label Apr 3, 2025
@darshankawar
Copy link
Member

4. Purchase the "upgrade", then click "restore purchases". If you don't see the log line, restart and retry. For me this is nondeterministic, but if you try this a few times you may see the error log. I also have better luck seeing the error if I run it from XCode directly.

Alternative path to reproduce: 4a. Launch from XCode. Purchase the "Upgrade", then refund it in XCode's Transactions log. Restart and repeat as necessary. The error is logged just after the refund is issued.

Thanks for the report @edpizzi, I tried to follow this by using both these approaches, but I wasn't able to replicate the reported error after trying to purchase it few times. Maybe I am missing some steps / config to properly verify it, that I'll try to see if I can get it working to replicate properly.

Meantime, I'll keep the issue open and label for team's attention on the reported behavior and error triggered.

@darshankawar darshankawar added platform-ios iOS applications specifically p: in_app_purchase Plugin for in-app purchase package flutter/packages repository. See also p: labels. team-ios Owned by iOS platform team and removed in triage Presently being triaged by the triage team labels Apr 3, 2025
@edpizzi
Copy link
Author

edpizzi commented Apr 5, 2025

I did a bit more investigating. I edited the code to print whether we're in the main thread when sending transaction update messages back to Flutter (flutter/packages@be9a9b9).

What I find is the following (launched from XCode): if I make a purchase, the transaction update sends from the main thread (safe).

When I refund a purchase (XCode UI) or verify purchases nontrivally (example app UI, but only if there are purchases to trigger the callback), I see the update come from a non-main thread (unsafe):

sendTransactionUpdate: other thread
[ERROR:flutter/shell/common/shell.cc(1053)] The 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchase2CallbackAPI.onTransactionsUpdated' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel.
See https://docs.flutter.dev/platform-integration/platform-channels#channels-and-platform-threading for more information.

Looking at the code, we create a Task to listen for transaction updates. My iOS dev is weak, but I understand that the queue / thread a task is assigned to depends on the context. If these tasks are assigned to a background queue, then this would be unsafe without jumping back to the main thread to send to the channel.

If I set a debugger when this happens via restorePurchases, I see we're in a thread for a com.apple.root.user-initiated-qos.cooperative Queue, and the stack trace starts with the Task in restorePurchases, so it appears that at least sometimes, these Tasks are on a background queue.

So I don't know why it's not consistently reproducible (although this line will only get logged in debug builds), but the problem and the fix seem pretty clear. Here's a basic fix:

flutter/packages@e450fdd

All of my debugging can be found in this branch: https://github.com/edpizzi/flutter-packages/tree/repro-sk2-threading
This branch has just the fix: https://github.com/edpizzi/flutter-packages/tree/iap-storekit2-channel-thread

I'm happy to clean this up into a PR if this is a reasonable approach, or let the team address it.

Just mentioning #116383, the main bug tracking adding StoreKit2 support, so that these are linked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p: in_app_purchase Plugin for in-app purchase package flutter/packages repository. See also p: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team
Projects
None yet
2 participants