ground-android icon indicating copy to clipboard operation
ground-android copied to clipboard

LocationOfInterestRepository.getOfflineLoi java.lang.IllegalStateException - LOI not found

Open anandwana001 opened this issue 1 year ago • 16 comments

 Fatal Exception: java.lang.IllegalStateException: LOI not found: uJcwRNB9BB7yPwGhrKF6
       at com.google.android.ground.repository.LocationOfInterestRepository.getOfflineLoi(LocationOfInterestRepository.kt:93)
       at com.google.android.ground.repository.LocationOfInterestRepository$getOfflineLoi$1.invokeSuspend(:15)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
       at android.os.Handler.handleCallback(Handler.java:958)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:230)
       at android.os.Looper.loop(Looper.java:319)
       at android.app.ActivityThread.main(ActivityThread.java:8913)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
        

Total events by version 0.1.10 (20) 15 for the last 7 days

anandwana001 avatar Sep 30 '24 13:09 anandwana001

@shobhitagarwal1612 is this the correct user behaviour?

as per the code

 suspend fun getOfflineLoi(surveyId: String, loiId: String): LocationOfInterest {
    val survey = localSurveyStore.getSurveyById(surveyId) ?: error("Survey not found: $surveyId")
    return localLoiStore.getLocationOfInterest(survey, loiId) ?: error("LOI not found: $loiId")
  }

it should crash, but, is it good to crash or should we do something better for the user behaviour?

anandwana001 avatar Sep 30 '24 13:09 anandwana001

Are you sure that the application is crashing?

shobhitagarwal1612 avatar Sep 30 '24 13:09 shobhitagarwal1612

it says Fatal Exception, so I am assuming its an app crash. And, error("LOI not found: $loiId") throw exception so it should crash.

WDYT?

BTW, the stack trace is from firebase.

anandwana001 avatar Sep 30 '24 16:09 anandwana001

From crash logs I found that this is happening in SyncStatusFragment. In this particular usecase, the code isn't wrapped around a try-catch block. So the app must be getting crashed when opening the sync status screen. Can you please try to understand how this is happening and the root cause for the LOIs to be missing?

shobhitagarwal1612 avatar Sep 30 '24 17:09 shobhitagarwal1612

@anandwana001 is this one still in progress by you?

kenstershiro avatar Nov 06 '24 15:11 kenstershiro

Part 1 on crashes resolved. Suggestion to check logs to determine the LOI affected and whether they are orphaned from the upload synch. Happening on 9 different surveys

kenstershiro avatar Nov 07 '24 15:11 kenstershiro

@anandwana001 Hint in logs breadcrumbs in Firebase Crashlytics:

ignoring invalid submission mutation: Unknown jobId YZTumCFHZUNhdIlbwOrK in submission mutation 99

gino-m avatar Nov 07 '24 15:11 gino-m

Request to prioritize this before next Field trip @jabramowitz5 FYI

kenstershiro avatar Nov 07 '24 15:11 kenstershiro

Edge case to test:

  • Activate airplane mode
  • Collect data in Android app
  • Delete related job in web interface (or add some debug code to delete locally)
  • Wait for local survey to be updated
  • Reenable airplane mode

What happens when the job is deleted before the pending mutations are synced?

gino-m avatar Nov 07 '24 15:11 gino-m

LOI 51cJrOKVaRsZFH6isObK in remote survey HZq3OSSYqRIhwsvVWqQH is invalid (Ask Gemini)
                                                                                                    com.google.android.ground.persistence.remote.DataStoreException: Missing job dkpfwqijB7UQ0dYEbLzJ
                                                                                                    	at com.google.android.ground.persistence.remote.DataStoreException$Companion.checkNotNull(DataStoreException.kt:23)
                                                                                                    	at com.google.android.ground.persistence.remote.firebase.schema.LoiConverter.toLoiUnchecked(LoiConverter.kt:45)
                                                                                                    	at com.google.android.ground.persistence.remote.firebase.schema.LoiConverter.toLoi-gIAlu-s(LoiConverter.kt:35)
                                                                                                    	at com.google.android.ground.persistence.remote.firebase.schema.LoiCollectionReference$toLois$2.invokeSuspend(LoiCollectionReference.kt:66)
                                                                                                    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                                                    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
2024-11-08 12:54:14.787 10006-10377 LoiCollect...nce$toLois com.google.android.ground            W  LOI VqAc6Ka5HGUoVmNIIhBd in remote survey HZq3OSSYqRIhwsvVWqQH is invalid (Ask Gemini)
                                                                                                    com.google.android.ground.persistence.remote.DataStoreException: Missing job N7FCsKkYswbqR6uT4jq2
                                                                                                    	at com.google.android.ground.persistence.remote.DataStoreException$Companion.checkNotNull(DataStoreException.kt:23)
                                                                                                    	at com.google.android.ground.persistence.remote.firebase.schema.LoiConverter.toLoiUnchecked(LoiConverter.kt:45)
                                                                                                    	at com.google.android.ground.persistence.remote.firebase.schema.LoiConverter.toLoi-gIAlu-s(LoiConverter.kt:35)
                                                                                                    	at com.google.android.ground.persistence.remote.firebase.schema.LoiCollectionReference$toLois$2.invokeSuspend(LoiCollectionReference.kt:66)
                                                                                                    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                                                    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
                                                                                                    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

coming after testing on deleted project after collecting data @shobhitagarwal1612

anandwana001 avatar Nov 08 '24 07:11 anandwana001

Reopening to ensure we figure out the root cause to avoid data loss.

gino-m avatar Nov 08 '24 15:11 gino-m

I suspect this is also related to the race condition of Survey and LOIs not being loaded before the data collection fragment is rendered.

Suggestion: Wrap the DataCollectionFragment in a container fragment which shows a loading spinner while survey and LOIs are loading.

gino-m avatar Aug 07 '25 12:08 gino-m

You should put null skipper on surveyId as survey id is not present in the data store, or you need to refresh data store or add job data in a app cache!

himanshukar avatar Nov 26 '25 19:11 himanshukar

You should put null skipper on surveyId as survey id is not present in the data store, or you need to refresh data store or add job data in a app cache!

  • If the user is offline there's no way to refresh the data store.
  • The job metadata (not data) is expected to be stored locally before this method is called.
  • As mentioned, the core issue is likely that the job metadata isn't loaded from the local store before this flow is triggered.

gino-m avatar Nov 26 '25 19:11 gino-m

As you said the job metadata is expected to be stored locally before this method is called,A Server side hook can sync meta data while connection is still active.

himanshukar avatar Nov 26 '25 20:11 himanshukar

As you said the job metadata is expected to be stored locally before this method is called,A Server side hook can sync meta data while connection is still active.

I'm not sure what you mean by "a server side hook" here, but the client already kept up-to-date via Firebase Messaging. Please submit feedback in the form of concrete PRs going forward so that we can better understand your suggestions in context.

gino-m avatar Nov 26 '25 21:11 gino-m