Repository navigation
[cloud_firestore] INTERNAL ASSERTION FAILED: A transaction object cannot be used after its update callback has been invoked. #1969
Description
Activity
Seems to same here. iOS is good. Only android appear.
2020-02-11 15:19:18.335 13830-14111/? E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #6 Process: live.effy.app.dev, PID: 13830 java.lang.RuntimeException: An error occurred while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:354) at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383) at java.util.concurrent.FutureTask.setException(FutureTask.java:252) at java.util.concurrent.FutureTask.run(FutureTask.java:271) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764) Caused by: java.lang.AssertionError: INTERNAL ASSERTION FAILED: A transaction object cannot be used after its update callback has been invoked. at com.google.firebase.firestore.util.Assert.fail(com.google.firebase:firebase-firestore@@21.3.0:46) at com.google.firebase.firestore.util.Assert.hardAssert(com.google.firebase:firebase-firestore@@21.3.0:31) at com.google.firebase.firestore.core.Transaction.ensureCommitNotCalled(com.google.firebase:firebase-firestore@@21.3.0:246) at com.google.firebase.firestore.core.Transaction.lookup(com.google.firebase:firebase-firestore@@21.3.0:81) at com.google.firebase.firestore.Transaction.getAsync(com.google.firebase:firebase-firestore@@21.3.0:191) at com.google.firebase.firestore.Transaction.get(com.google.firebase:firebase-firestore@@21.3.0:228) at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$5.doInBackground(CloudFirestorePlugin.java:569) at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$5.doInBackground(CloudFirestorePlugin.java:564) at android.os.AsyncTask$2.call(AsyncTask.java:333) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764)Reacted by forsythe, teehtheg, Niku Alla, AnSLEX, Gustavo Massaneiro and yizenlimThis happens to me when running multiple transactions.
Reacted by forsythe, Niku Alla, AnSLEX, yizenlim, TaeJung Shim and Gustavo MassaneiroThis also happens to me when running the same transaction on two android devices
I see this as well - and unfortunately including catchError() on runTransaction does not seem to protect against this crash.
Reacted by forsythe, Todd Lee and AnSLEXHi, anybody found a solution for this ?
Experiencing the same issue (see #1216)
Despite the title of this issue, it happens on both android and iOS
Reacted by Todd LeeTracked this down to Firebase offline use and Transactions.
This will happen when you are using either BATCHWRITES or runTransaction in ANDROID and you have no INTERNET connection/limited Internet connection/or unstable internet connection at the point of where the transaction runs.
So to reproduce, run your code that uses either a runTransaction (Ensure that internet is not on). By default, TX require ONLINE access and should FAIL immediately where you can catch the exception. For iOS an exception is raised. For Android - no exception is raised and the transaction trys to run regardless.
No wait a while (5-10 secs) , then turn on INTERNET - after about 10 or so seconds when connection re-establishes itself, FLUTTER crashes.
I dont get this happening in iOS. I recall this working some time back so expect this to be a configuration/regression issue with cloud_firestore and potentially other plugins.
This is my setup :
cloud_firestore: 0.13.4 firebase_auth: 0.15.4 and : dependency_overrides: firebase_core: 0.4.4Build gradle (Snapshot)
dependencies { classpath 'com.android.tools.build:gradle:3.5.0' 3.5.3 classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta02' }and my build.gradle in Android/app :
dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' implementation 'com.google.firebase:firebase-analytics:17.2.2' implementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta01' }This is CRITICAL. I cannot release to the PLAYSTORE with this issue.
Reacted by sudolukelucas, Josh Rice, AnSLEX, Korneliusz Tombarkiewicz, Winston Yeo, sallypeters, yizenlim, Tim Rots, Kelvin Bulwinkel, Zein Ersyad and 1 moreReacted by sudolukelucas, sallypeters, yizenlim, Kelvin Bulwinkel and Zein ErsyadThis happens to me when running multiple transactions.
Try not putting things unrelated with transaction inside it, it actually solved my issue.
- addedimpact: crowdAffects many people, though not necessarily a specific customer with an assigned label. (P2)Affects many people, though not necessarily a specific customer with an assigned label. (P2)
on Apr 20, 2020 I made the Transaction function non-async (chained futures using then instead) and it solved this problem for me. Eg. changed this:
Firestore.instance.runTransaction((Transaction tx) async { DocumentSnapshot recipeeSnapshot = await tx.get(recipee.reference); if (recipeeSnapshot.exists) { await tx.update(recipee.reference, <String, dynamic>{ 'in_favorites_of': FieldValue.arrayUnion([userReference]), }); } })into this:
Firestore.instance .runTransaction((Transaction tx) { return tx.get(recipee.reference).then((recipeeSnapshot) { if (recipeeSnapshot.exists) { return tx.update(recipee.reference, <String, dynamic>{ 'in_favorites_of': FieldValue.arrayUnion([userReference]), }); } }); })I just noticed that the example on running transactions on the official readme uses an async transaction, I'm not sure if this is the best way to do it since concurrent transactions with async will cause this crash. Eg. tapping a button very fast that triggers the same transaction.
Reacted by Jagraj SinghI made the Transaction function non-async (chained futures using then instead) and it solved this problem for me. Eg. changed this:
Firestore.instance.runTransaction((Transaction tx) async { DocumentSnapshot recipeeSnapshot = await tx.get(recipee.reference); if (recipeeSnapshot.exists) { await tx.update(recipee.reference, <String, dynamic>{ 'in_favorites_of': FieldValue.arrayUnion([userReference]), }); } })into this:
Firestore.instance .runTransaction((Transaction tx) { return tx.get(recipee.reference).then((recipeeSnapshot) { if (recipeeSnapshot.exists) { return tx.update(recipee.reference, <String, dynamic>{ 'in_favorites_of': FieldValue.arrayUnion([userReference]), }); } }); })This did not work for me. I tried to set the transactions to be non-async, but running the transactions simultaneously from two different devices still would crash one of them.
I'm having the same issue. Just a thought, but should we try putting
awaitin front of the transaction call? Like this:await Firestore.instance.runTransaction((Transaction tx) async { DocumentSnapshot recipeeSnapshot = await tx.get(recipee.reference); if (recipeeSnapshot.exists) { await tx.update(recipee.reference, <String, dynamic>{ 'in_favorites_of': FieldValue.arrayUnion([userReference]), }); } })29 remaining items
Hi everyone. So, I rolled back to 0.12.10+2 and although my app doesn't crash anymore if I fire 2 transactions one on iOS and one on Android physical devices at exactly the same time the Android device fails to get its transaction persisted in Firebase. Which I think it's even worse because I don't get any exceptions and me and the user are none the wiser. I rather let the app crash so the user knows something went wrong. I'm safeguarding his order so he'll see it's still there and will be able to resubmit. I haven't looked at all the code snippets posted yet. Maybe I'll find some wisdom there.
From your description - this is what I would expect to happen apart from no exception being propagated back to the android client when its TX fails - but that could be down to your implementation .
If two users update the same document at exactly the same time - Are you expecting Cloud Firestore to deny one user access , fail the tx and allow the other user 100% write access to the document ?
What actually happens is that in the case of any potential concurrent edits, Cloud Firestore will attempt to re-run the entire TX again but this time it will (based on how you have structured your tx code) attempt to get the latest documents so its working on the most uptodate data. It will attempt to do this 5 times and if the tx still finds its working with dirty data, the entire tx will fail. This is the correct scenario.
I suspect that your android client is indeed getting its data persisted but then the iOS client now gets the most uptodate document and overwrites the data persisted by the Android client. This can easily be proved amending your logic to update different parts of the document based on the client that is running the TX (Android/iOS) - then checking to see that iOS field has been updated and Android field has been updated after the TX completes etc ...
If you want your TX code to fail if the document its working on is dirty (Has been changed by another user) then you can provide this functionality yourself - this is an implementation issue and nothing to do with issues with Cloud Firestore.
Reacted by bpaul7101@MsXam thank you for your comments. Interesting scenario you mentioned that the Android transaction is overwritten by the iOS client. I will test that. I'm still very new to Firebase but from my previous SQL knowledge I would expect the database to kind of queue the incoming transactions and process them serially as they are finished, but that's how I picture it in my head and it could well be a wrong assumption. It looks like more than one transaction can be processed concurrently, am I right? Then yes, I totally agree I would need to look at my logic and rewrite accordingly. Thanks again.
I have downgraded to the recommended version and can confirm BOTH Android & iOS work.
Many thanks for spending the time going through the various commits to establish a version that works.
@MsXam . Downgrading works. I no longer have the crash.
@Ehesp Setting a timeout to say - 5 seconds crashes an iOS or Android app when the TX doesnt complete within this time. Has this been identified as a bug also on your refactoring analysis - the docs state that a transaction that fails should ALWAYS return an error code. This currently is not happening on both platforms.
@bpaul7101 added a test case on that on the rework:
Just curious has this issue been addressed as part of the flutterfire roadmap ? #2913
Hey everyone 👋 , as part of our on-going work for #2582, this has been resolved in our Firebase Firestore rework (#2913), transactions has had a massive rework as unfortunately it was fundamentally broken in a lot of places, points specifically in relation to this issue (but there's many others):
- transactions will now bubble errors through to Dart should it fail on native (rather than it crashing).
- the default timeout has been increased to 30 seconds, up from 5.
- timeout behaviour is now functioning correctly on iOS (previously on timeout in iOS nothing happened as the return value from
dispatch_semaphore_waitwas completely ignored). - iOS no longer returns a result twice.
This has now been merged into master. We'll look at publishing some prereleases in the next few days. Thank you
@Salakar Are you sure this is in Stable ? I have just tried to see if this has been fixed and I still get the same issue as discussed here #1969 (comment)
i.e I get a crash in Android when device toggles between offline and online and there are transactions already running ....
@Salakar Are you sure this is in Stable ? I have just tried to see if this has been fixed and I still get the same issue as discussed here #1969 (comment)
i.e I get a crash in Android when device toggles between offline and online and there are transactions already running ....
Nope it's not in stable;
This has now been merged into master. We'll look at publishing some prereleases in the next few days. Thank you
@Salakar , sorry I meant to say master ...
any updates ?
- locked and limited conversation to collaborators
on Aug 8, 2020

Describe the bug
App crash when adding new data. It doesn't print any error in flutter developement. But if you open android log it show:
java.lang.RuntimeException: An error occurred while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:318) at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354) at java.util.concurrent.FutureTask.setException(FutureTask.java:223) at java.util.concurrent.FutureTask.run(FutureTask.java:242) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761) Caused by: java.lang.AssertionError: INTERNAL ASSERTION FAILED: A transaction object cannot be used after its update callback has been invoked. at com.google.firebase.firestore.util.Assert.fail(com.google.firebase:firebase-firestore@@21.3.0:46) at com.google.firebase.firestore.util.Assert.hardAssert(com.google.firebase:firebase-firestore@@21.3.0:31) at com.google.firebase.firestore.core.Transaction.ensureCommitNotCalled(com.google.firebase:firebase-firestore@@21.3.0:246) at com.google.firebase.firestore.core.Transaction.lookup(com.google.firebase:firebase-firestore@@21.3.0:81) at com.google.firebase.firestore.Transaction.getAsync(com.google.firebase:firebase-firestore@@21.3.0:191) at com.google.firebase.firestore.Transaction.get(com.google.firebase:firebase-firestore@@21.3.0:228) at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$5.doInBackground(CloudFirestorePlugin.java:569) at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$5.doInBackground(CloudFirestorePlugin.java:564) at android.os.AsyncTask$2.call(AsyncTask.java:304) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761)To Reproduce
Steps to reproduce the behavior:
This is so occasional.
Expected behavior
App should not crash