Android Crash - "Callback arg cannot be called more than once";
Report
Plugin Version
6.15.1
On what Platform are you having the issue?
Android only
What did you do?
We use logEvent
What did you expect to happen?
logEvent callbacks not to crash on Android
What happened instead?
On new arch in production for Android we receive crash logs where onError callback is called, but invoked twice which causes a crash to trigger;
react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp
if (!callback) {
LOG(FATAL) << "Callback arg cannot be called more than once";
return;
}
#10 pc 0x000000000060cb00 /data/app/~~fd4r20TdR3MOOG2sjAaVtg==/com.wealthsimple.trade-dfI26diei0-qwrUPNR0eeg==/base.apk (com.facebook.react.bridge.CxxCallbackImpl.invoke+8)
#11 pc 0x0000000000780ee4 /apex/com.android.art/lib64/libart.so (nterp_helper+7540)
#12 pc 0x0000000000879a18 /data/app/~~fd4r20TdR3MOOG2sjAaVtg==/com.*.*-dfI26diei0-qwrUPNR0eeg==/base.apk (com.appsflyer.reactnative.RNAppsFlyerModule$3.onError+12)
#13 pc 0x0000000000780ee4 /apex/com.android.art/lib64/libart.so (nterp_helper+7540)
#14 pc 0x0000000000861500 /data/app/~~fd4r20TdR3MOOG2sjAaVtg==/com.*.*-dfI26diei0-qwrUPNR0eeg==/base.apk (com.appsflyer.internal.AFf1pSDK.AFAdRevenueData+116)
Please provide any other relevant information.
Im not sure how the double invocation happens, based on google play console it happens mostly on backgrounding. I could repro the issue by manually double invoking a callback. Based on the codepaths I see that we trigger the error callback in the try / catch as well as onError, there might be some type of condition being hit here where the error happens in logEvent natively gets thrown up and handled again?
@amit-kremer93
Same with 6.15.3 (expo 52.0.31), we're slowly rolling out a new version and seeing a few of these
The stack trace looks like this
==/base.apk (com.facebook.react.bridge.CxxCallbackImpl.invoke+8)
==/base.apk (com.appsflyer.reactnative.RNAppsFlyerModule$3.onError+12)
==/base.apk (com.appsflyer.internal.AFf1oSDK.getCurrencyIso4217Code+116)
==/base.apk (com.appsflyer.internal.AFe1eSDK.component3+96)
@markstreich we disabled the double callback as a local patch for remediation
@mobinni could you share the patch?
Something like this? (I don't know how to recreate it, and don't know java, so kind of flying blind)
--- a/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
+++ b/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
@@ -326,7 +326,7 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
});
}
} catch (Exception e) {
- errorCallback.invoke(e.getMessage());
+ // errorCallback.invoke(e.getMessage());
return;
}
}
diff --git a/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java b/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
index 3892ba5..51cca6b 100755
--- a/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
+++ b/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
@@ -304,7 +304,8 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
final Callback errorCallback) {
try {
if (eventName.trim().equals("")) {
- errorCallback.invoke(NO_EVENT_NAME_FOUND);
+ // Disable as this is not a valuable error callback
+ // errorCallback.invoke(NO_EVENT_NAME_FOUND);
return;
}
Map<String, Object> data = RNUtil.toMap(eventData);
@@ -316,18 +317,17 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
AppsFlyerLib.getInstance().logEvent(getCurrentActivity(), eventName, data, new AppsFlyerRequestListener() {
@Override
public void onSuccess() {
- successCallback.invoke(SUCCESS);
+ // successCallback.invoke(SUCCESS);
}
@Override
public void onError(int i, @NonNull String s) {
- errorCallback.invoke(s);
+ // errorCallback.invoke(s);
}
});
}
} catch (Exception e) {
- errorCallback.invoke(e.getMessage());
- return;
+ // Do nothing
}
}
@@ -336,7 +336,8 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
final String eventName, ReadableMap eventData, final Promise promise) {
try {
if (eventName.trim().equals("")) {
- promise.reject(NO_EVENT_NAME_FOUND, new Exception(NO_EVENT_NAME_FOUND).getMessage());
+ // Disable as this is not a valuable error callback
+ // promise.reject(NO_EVENT_NAME_FOUND, new Exception(NO_EVENT_NAME_FOUND).getMessage());
return;
}
Map<String, Object> data = RNUtil.toMap(eventData);
@@ -348,22 +349,21 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
AppsFlyerLib.getInstance().logEvent(getCurrentActivity(), eventName, data, new AppsFlyerRequestListener() {
@Override
public void onSuccess() {
- promise.resolve(SUCCESS);
+ // promise.resolve(SUCCESS);
}
@Override
public void onError(int i, @NonNull String s) {
- promise.reject(s);
+ // promise.reject(s);
}
});
}
} catch (Exception e) {
- promise.reject(UNKNOWN_ERROR, e);
- return;
+ // Do nothing
}
}
Thanks!
@al-af can you please take a look at this
Same with
6.15.3(expo 52.0.31), we're slowly rolling out a new version and seeing a few of theseThe stack trace looks like this
==/base.apk (com.facebook.react.bridge.CxxCallbackImpl.invoke+8) ==/base.apk (com.appsflyer.reactnative.RNAppsFlyerModule$3.onError+12) ==/base.apk (com.appsflyer.internal.AFf1oSDK.getCurrencyIso4217Code+116) ==/base.apk (com.appsflyer.internal.AFe1eSDK.component3+96)
I have the same problem too (with version 6.16.2, expo 52.0.46), I don't understand why in the crash logs there is a reference to this method: getCurrencyIso4217Code when it doesn't exist in the React Native plugin or in the Android SDK.
I have hundreds of crashes in production because of this.
See my crashlytics report
com.appsflyer.reactnative.RNAppsFlyerModule$3.onError + 12
com.appsflyer.internal.AFf1oSDK.getCurrencyIso4217Code + 116
com.appsflyer.internal.AFe1eSDK.component3 + 96
com.appsflyer.internal.AFe1aSDK$4.run + 210
This might be deep inside appsflyer itself - https://github.com/search?q=org%3AAppsFlyerSDK%20getCurrencyIso4217Code&type=code
At least for me, one kind of "solution" to this issue, is to use the non-callback version of the logEvent function, since there are these two signatures for the function:
logEvent(eventName: string, eventValues: object): Promise<string>;
logEvent(
eventName: string,
eventValues: object,
successC: SuccessCB,
errorC: ErrorCB
): void;
If I only give the first two parameters to the function, then it uses the version that returns a promise and you can get the result from the promise, and that version of the function doesn't seem to have this issue, probably because it doesn't do callbacks.
I was able to reproduce the issue by disabling the device’s internet connection, opening the app, and performing the action that triggers AppsFlyer’s logEvent multiple times. From my investigation, it seems the library keeps references to old AppsFlyerRequestListener instances internally, so when an error occurs, it invokes those stale listeners again, which eventually causes the crash.
Here is my patch, hope it helps:
diff --git a/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java b/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
index bb77d93..dbe7a93 100755
--- a/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
+++ b/node_modules/react-native-appsflyer/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
@@ -54,6 +54,7 @@ import java.util.List;
import java.util.Map;
import java.util.Iterator;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
import static com.appsflyer.reactnative.RNAppsFlyerConstants.*;
import static com.appsflyer.reactnative.RNAppsFlyerConstants.afOnDeepLinking;
@@ -323,14 +324,20 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
Activity currentActivity = getCurrentActivity();
if (currentActivity != null) {
AppsFlyerLib.getInstance().logEvent(getCurrentActivity(), eventName, data, new AppsFlyerRequestListener() {
+ final AtomicBoolean isCompleted = new AtomicBoolean(false);
+
@Override
public void onSuccess() {
- successCallback.invoke(SUCCESS);
+ if (isCompleted.compareAndSet(false, true)) {
+ successCallback.invoke(SUCCESS);
+ }
}
@Override
public void onError(int i, @NonNull String s) {
- errorCallback.invoke(s);
+ if (isCompleted.compareAndSet(false, true)) {
+ errorCallback.invoke(String.valueOf(i), s);
+ }
}
});
}
@@ -355,14 +362,20 @@ public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
Activity currentActivity = getCurrentActivity();
if (currentActivity != null) {
AppsFlyerLib.getInstance().logEvent(getCurrentActivity(), eventName, data, new AppsFlyerRequestListener() {
+ final AtomicBoolean isCompleted = new AtomicBoolean(false);
+
@Override
public void onSuccess() {
- promise.resolve(SUCCESS);
+ if (isCompleted.compareAndSet(false, true)) {
+ promise.resolve(SUCCESS);
+ }
}
@Override
public void onError(int i, @NonNull String s) {
- promise.reject(s);
+ if (isCompleted.compareAndSet(false, true)) {
+ promise.reject(String.valueOf(i), s);
+ }
}
});
}
I have same issue:
2025-09-19 11:45:47.967 10827-10827 DEBUG pid-10827 A #27 pc 0000000000006680 [anon:dalvik-classes12.dex extracted in memory from /data/app/~~vRxBu-fQnWBeoXmmm8vdCA==/PACKAGE-KkfU5H2oxLWpJPHPmZV8VQ==/base.apk!classes12.dex] (com.appsflyer.reactnative.RNAppsFlyerModule$3.onError+12)
2025-09-19 11:45:47.967 10827-10827 DEBUG pid-10827 A #30 pc 00000000004074fe [anon:dalvik-classes21.dex extracted in memory from /data/app/~~vRxBu-fQnWBeoXmmm8vdCA==/PACKAGE-KkfU5H2oxLWpJPHPmZV8VQ==/base.apk!classes21.dex] (com.appsflyer.internal.AFe1eSDK.getMediationNetwork+146)
2025-09-19 11:45:47.967 10827-10827 DEBUG pid-10827 A #33 pc 0000000000409130 [anon:dalvik-classes21.dex extracted in memory from /data/app/~~vRxBu-fQnWBeoXmmm8vdCA==/PACKAGE-KkfU5H2oxLWpJPHPmZV8VQ==/base.apk!classes21.dex] (com.appsflyer.internal.AFe1lSDK.component3+96)
2025-09-19 11:45:47.967 10827-10827 DEBUG pid-10827 A #36 pc 0000000000409c92 [anon:dalvik-classes21.dex extracted in memory from /data/app/~~vRxBu-fQnWBeoXmmm8vdCA==/PACKAGE-KkfU5H2oxLWpJPHPmZV8VQ==/base.apk!classes21.dex] (com.appsflyer.internal.AFe1nSDK$4.run+210)
https://github.com/AppsFlyerSDK/appsflyer-react-native-plugin/issues/601#issuecomment-3311929157
This works :)
Thanks @TinhHuynh