Skip to content

[cloud_firestore] INTERNAL ASSERTION FAILED: A transaction object cannot be used after its update callback has been invoked. #1969

Description

@josephmangmang

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.

  1. Add new data.
  2. Get data

Expected behavior
App should not crash

Activity

  1. dfdgsdfg commented on Feb 11, 2020

    @dfdgsdfg

    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) 
    
  2. sterrenb commented on Feb 20, 2020

    @sterrenb

    This happens to me when running multiple transactions.

  3. forsythe commented on Feb 23, 2020

    @forsythe

    This also happens to me when running the same transaction on two android devices

  4. leetworx commented on Feb 25, 2020

    @leetworx

    I see this as well - and unfortunately including catchError() on runTransaction does not seem to protect against this crash.

  5. mustafma commented on Feb 27, 2020

    @mustafma

    Hi, anybody found a solution for this ?

  6. teehtheg commented on Mar 1, 2020

    @teehtheg

    Experiencing the same issue (see #1216)

    Despite the title of this issue, it happens on both android and iOS

  7. bpaul7101 commented on Mar 14, 2020

    @bpaul7101

    Tracked 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.4
    

    Build 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.

  8. Nikualla1 commented on Apr 15, 2020

    @Nikualla1

    This happens to me when running multiple transactions.

    Try not putting things unrelated with transaction inside it, it actually solved my issue.

  9. added
    impact: crowdAffects many people, though not necessarily a specific customer with an assigned label. (P2)
    on Apr 20, 2020
  10. MauScheff commented on Apr 20, 2020

    @MauScheff

    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]), }); } }); })

  11. MauScheff commented on Apr 20, 2020

    @MauScheff

    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.

  12. forsythe commented on Apr 24, 2020

    @forsythe

    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]), }); } }); })

    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.

  13. mohisham commented on Apr 26, 2020

    @mohisham

    I'm having the same issue. Just a thought, but should we try putting await in 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]), }); } })

  14. 29 remaining items

  15. ErnestoCuesy commented on Jun 7, 2020

    @ErnestoCuesy

    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.

  16. MsXam commented on Jun 7, 2020

    @MsXam

    @ErnestoCuesy

    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.

  17. ErnestoCuesy commented on Jun 8, 2020

    @ErnestoCuesy

    @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.

  18. alexda12 commented on Jun 8, 2020

    @alexda12

    @MsXam

    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.

  19. bpaul7101 commented on Jun 12, 2020

    @bpaul7101

    @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.

  20. Ehesp commented on Jun 12, 2020

    @Ehesp
    Member

    @bpaul7101 added a test case on that on the rework:

    image

  21. yizenlim commented on Jul 8, 2020

    @yizenlim

    Just curious has this issue been addressed as part of the flutterfire roadmap ? #2913

  22. Salakar commented on Jul 8, 2020

    @Salakar
    Contributor

    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_wait was 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

  23. alexda12 commented on Jul 8, 2020

    @alexda12

    @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 ....

  24. Salakar commented on Jul 8, 2020

    @Salakar
    Contributor

    @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

  25. alexda12 commented on Jul 8, 2020

    @alexda12

    @Salakar , sorry I meant to say master ...

  26. habibmhamadi commented on Jul 20, 2020

    @habibmhamadi

    any updates ?

  27. locked and limited conversation to collaborators on Aug 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    impact: crowdAffects many people, though not necessarily a specific customer with an assigned label. (P2)plugin: cloud_firestoretype: bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions