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

getTaskViewModel - Collection contains no element matching the predicate.

Open anandwana001 opened this issue 11 months ago • 22 comments

Caused by java.util.NoSuchElementException: Collection contains no element matching the predicate.
       at com.google.android.ground.ui.datacollection.DataCollectionViewModel.getTaskViewModel(DataCollectionViewModel.kt:467)
       at com.google.android.ground.ui.datacollection.DataCollectionViewModel.saveCurrentState(DataCollectionViewModel.kt:252)
       at com.google.android.ground.ui.datacollection.DataCollectionFragment.onPause(DataCollectionFragment.kt:117)
       at androidx.fragment.app.Fragment.performPause(Fragment.java:3330)
       at androidx.fragment.app.FragmentStateManager.pause(FragmentStateManager.java:692)
       at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:318)
       at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:114)
       at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1675)
       at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3259)
       at androidx.fragment.app.FragmentManager.dispatchPause(FragmentManager.java:3195)
       at androidx.fragment.app.Fragment.performPause(Fragment.java:3323)
       at androidx.fragment.app.FragmentStateManager.pause(FragmentStateManager.java:692)
       at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:318)
       at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:114)
       at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1675)
       at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3259)
       at androidx.fragment.app.FragmentManager.dispatchPause(FragmentManager.java:3195)
       at androidx.fragment.app.FragmentController.dispatchPause(FragmentController.java:296)
       at androidx.fragment.app.FragmentActivity.onPause(FragmentActivity.java:284)
       at com.google.android.ground.AbstractActivity.onPause(AbstractActivity.kt:63)
       at android.app.Activity.performPause(Activity.java:8870)
       at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1618)
       at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:5722)
       at android.app.ActivityThread.performPauseActivity(ActivityThread.java:5683)
       at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:5635)
       at android.app.servertransaction.PauseActivityItem.execute(PauseActivityItem.java:47)
       at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
       at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2584)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8810)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
        

anandwana001 avatar Jan 13 '25 06:01 anandwana001

@shobhitagarwal1612 Will this get solved in any ongoing PR?

anandwana001 avatar Jan 13 '25 07:01 anandwana001

You can assign this one to me. I'm already working towards improving the reliability of this class. Will check if this error can be handled along with my changes or not.

shobhitagarwal1612 avatar Jan 13 '25 08:01 shobhitagarwal1612

@shobhitagarwal1612 Do you need any help with this ticket? Any observation so far, I can start from there

anandwana001 avatar Feb 12 '25 06:02 anandwana001

The error is originating from here. https://github.com/google/ground-android/blob/69234c3d90279c6c732905842e98b142cbaae168/app/src/main/java/com/google/android/ground/ui/datacollection/DataCollectionViewModel.kt#L201

We can either add some logging here to check for which task Id this is throwing error because it is not expected to happen ever.

shobhitagarwal1612 avatar Feb 12 '25 06:02 shobhitagarwal1612

@shobhitagarwal1612 happening for

screenName TextTaskFragment
selectedSurveyId JfFMvSENC7RXVSwZvw9L, G02xKGHAF4YFoKf01CpK, G02xKGHAF4YFoKf01CpK

anandwana001 avatar Feb 12 '25 07:02 anandwana001

My hunch is that it was previously happening due to "empty" task id which is no longer possible. Maybe we should do an error logging if the task isn't found anymore and return an empty view model. Wdyt?

shobhitagarwal1612 avatar Feb 12 '25 07:02 shobhitagarwal1612

@shobhitagarwal1612 I believe this is more likely due to a leaked viewmodel or race condition in code. In either case, we still haven't resolved the root case, so reopening this issue (see comment on https://github.com/google/ground-android/pull/3074#issuecomment-2813750034).

gino-m avatar Apr 17 '25 18:04 gino-m

Upon looking at the firebase logs, I found that this exception is accompanied with another exception "Missing job <job id>".

shobhitagarwal1612 avatar Apr 25 '25 09:04 shobhitagarwal1612

Will add more debug logs for better understanding.

shobhitagarwal1612 avatar Apr 25 '25 09:04 shobhitagarwal1612

@shobhitagarwal1612 At which point in DataCollectionFragment's lifecycle would the survey not be initialized?

Also, where is "Missing job " thrown? I only see "Missing job" (no ID) thrown in the converter when loading from remote, which shouldn't be getting used here. Any ideas?

gino-m avatar Apr 25 '25 13:04 gino-m

At which point in DataCollectionFragment's lifecycle would the survey not be initialized?

Survey is initialized. We can confirm this by the custom data logged by us (i.e. it contains a survey id).

Also, where is "Missing job " thrown? I only see "Missing job" (no ID) thrown in the converter when loading from remote, which shouldn't be getting used here. Any ideas?

This is logged during sync loi stage which happens asynchronously.

shobhitagarwal1612 avatar Apr 25 '25 14:04 shobhitagarwal1612

Survey is initialized. We can confirm this by the custom data logged by us (i.e. it contains a survey id).

Could the logged value have been set before the survey was subsequently cleared?

This is logged during sync loi stage which happens asynchronously.

Could this be occurring when the job actually was removed remotely?

gino-m avatar Apr 25 '25 14:04 gino-m

This is now a very serious issue - @anandwana001 Could you please resolve ASAP?

Image

gino-m avatar Jun 09 '25 08:06 gino-m

The fix has been done, but not deployed so far. @rfontanarosa Could you please help us with the latest release commit?

anandwana001 avatar Jun 10 '25 08:06 anandwana001

High number of crashes on this still occuring

kenstershiro avatar Jun 13 '25 14:06 kenstershiro

Ping @anandwana001

gino-m avatar Jun 16 '25 08:06 gino-m

In the current release we have - https://github.com/openforis/ground-android/blob/96cf727bf328c22283a78f23f166817b83a97990/app/src/main/java/org/groundplatform/android/ui/datacollection/DataCollectionViewModel.kt#L203

fun getTaskViewModel(taskId: String): AbstractTaskViewModel? {
    val viewModels = taskViewModels.value

    val task = tasks.first { it.id == taskId }

    if (viewModels.containsKey(taskId)) {
      return viewModels[taskId]
    }

    return try {
      val viewModel = viewModelFactory.create(getViewModelClass(task.type))
      taskViewModels.value[task.id] = viewModel

      val taskData: TaskData? = if (shouldLoadFromDraft) getValueFromDraft(task) else null
      viewModel.initialize(job, task, taskData)
      taskDataHandler.setData(task, taskData)
      viewModel
    } catch (e: Exception) {
      Timber.e("ignoring task with invalid type: $task.type")
      null
    }
  }

where

 val task = tasks.first { it.id == taskId }

this is what is causing the crash, in the current master branch, this is fixed using

val task =
      tasks.firstOrNull { it.id == taskId }
        ?: error("Task not found. taskId=$taskId, jobId=$jobId, loiId=$loiId, surveyId=$surveyId")

can we try to release a patch with this fix? cc @shobhitagarwal1612

anandwana001 avatar Jun 16 '25 10:06 anandwana001

In the current release we have - https://github.com/openforis/ground-android/blob/96cf727bf328c22283a78f23f166817b83a97990/app/src/main/java/org/groundplatform/android/ui/datacollection/DataCollectionViewModel.kt#L203

fun getTaskViewModel(taskId: String): AbstractTaskViewModel? { val viewModels = taskViewModels.value

val task = tasks.first { it.id == taskId }

if (viewModels.containsKey(taskId)) {
  return viewModels[taskId]
}

return try {
  val viewModel = viewModelFactory.create(getViewModelClass(task.type))
  taskViewModels.value[task.id] = viewModel

  val taskData: TaskData? = if (shouldLoadFromDraft) getValueFromDraft(task) else null
  viewModel.initialize(job, task, taskData)
  taskDataHandler.setData(task, taskData)
  viewModel
} catch (e: Exception) {
  Timber.e("ignoring task with invalid type: $task.type")
  null
}

} where

val task = tasks.first { it.id == taskId } this is what is causing the crash, in the current master branch, this is fixed using

val task = tasks.firstOrNull { it.id == taskId } ?: error("Task not found. taskId=$taskId, jobId=$jobId, loiId=$loiId, surveyId=$surveyId") can we try to release a patch with this fix? cc [@shobhitagarwal1612](https://github.com/shobhitagarwal1612)

So rather than crashing in these case, the app would do what? Show a blank screen? Skip the task? From the data quality perspective this sounds more harmful than actually crashing.

gino-m avatar Jun 16 '25 10:06 gino-m

As per the stacktrace of the crash, it is only happening when in onPause state, hence the current ongoing data will not be saved locally, and the user has to input the data again.

override fun onPause() { ... viewModel.saveCurrentState()

 fun saveCurrentState() { ...  getTaskViewModel(taskId) ?: error("ViewModel not found for task $taskId")

anandwana001 avatar Jun 16 '25 10:06 anandwana001

@rfontanarosa Can you test and cut a release with this fix?

@anandwana001 Does the fact that this only happens on pause provide a clue as to why the task is not found?

gino-m avatar Jun 16 '25 11:06 gino-m

In the current release we have - https://github.com/openforis/ground-android/blob/96cf727bf328c22283a78f23f166817b83a97990/app/src/main/java/org/groundplatform/android/ui/datacollection/DataCollectionViewModel.kt#L203

fun getTaskViewModel(taskId: String): AbstractTaskViewModel? { val viewModels = taskViewModels.value

val task = tasks.first { it.id == taskId }

if (viewModels.containsKey(taskId)) {
  return viewModels[taskId]
}

return try {
  val viewModel = viewModelFactory.create(getViewModelClass(task.type))
  taskViewModels.value[task.id] = viewModel

  val taskData: TaskData? = if (shouldLoadFromDraft) getValueFromDraft(task) else null
  viewModel.initialize(job, task, taskData)
  taskDataHandler.setData(task, taskData)
  viewModel
} catch (e: Exception) {
  Timber.e("ignoring task with invalid type: $task.type")
  null
}

} where

val task = tasks.first { it.id == taskId } this is what is causing the crash, in the current master branch, this is fixed using

val task = tasks.firstOrNull { it.id == taskId } ?: error("Task not found. taskId=$taskId, jobId=$jobId, loiId=$loiId, surveyId=$surveyId") can we try to release a patch with this fix? cc @shobhitagarwal1612

This isn't a fix. The custom error message was added to help debug the problem by getting the various ids when the crash happens. Given that the error stacktrace doesn't contain this message, I think the released version doesn't contain this code. It'd be nice if there was a way to know which app version points to which commit id in master branch.

shobhitagarwal1612 avatar Jun 16 '25 12:06 shobhitagarwal1612

Understood, just wondering if we can move forward on a proper fix while waiting for a new release to be cut. @rfontanarosa for new release.

gino-m avatar Jun 16 '25 12:06 gino-m

@gino-m Still waiting to fix race condition between loading of survey metadata and restore from draft.

@anandwana001 @shobhitagarwal1612 Team was waiting for logging from release to diagnose. Thinks it was working before. @anandwana001 will try to debug.

gino-m avatar Jul 25 '25 14:07 gino-m

@anandwana001 It appears your fix worked. Thank you!

Image

gino-m avatar Jul 30 '25 10:07 gino-m