androidfirebasecrashlyticsandroid-architecture-navigationillegalargumentexception

Firebase Crashlytics Navigation action/destination cannot be found from the current destination


I'm building my own app in Android I'm using Navigation component to navigate between fragments. It's logged IllegalArgumentException: Navigation action/destination cannot be found from the current destination in Crashlytics of Firebase. Testers and I have tried the test many times but haven't been able to reproduce that problem and it didn't crash.

Here is the log:

# Crashlytics - Stack trace
# Application: com.highsecure.drawsketch.sketchpaint
# Platform: android
# Version: 1.3.7 (21)
# Issue: 5e74ae9f3447101130fd8f601b280d25
# Session: 66B3A1F5002300017A6E5585CD6044CF_DNE_0_v2
# Date: Wed Aug 07 2024 23:33:58 GMT+0700 (Indochina Time)

Fatal Exception: java.lang.IllegalArgumentException: Navigation action/destination com.highsecure.drawsketch.sketchpaint:id/action_mainFragment_to_dashBoardFragment cannot be found from the current destination Destination(com.highsecure.drawsketch.sketchpaint:id/dashBoardFragment) label=fragment_dash_board class=com.example.ardrawsketch.ui.dashboard.DashBoardFragment
       at androidx.navigation.NavController.navigate(NavController.kt:1691)
       at androidx.navigation.NavController.navigate(NavController.kt:1609)
       at androidx.navigation.NavController.navigate(NavController.kt:1591)
       at androidx.navigation.NavController.navigate(NavController.kt:1574)
       at com.example.ardrawsketch.ui.activity.MainActivity$setupViews$1$1$1.invokeSuspend(MainActivity.kt:54)
       at com.example.ardrawsketch.ui.activity.MainActivity$setupViews$1$1$1.invoke(:12)
       at com.example.ardrawsketch.ui.activity.MainActivity$setupViews$1$1$1.invoke(:8)
       at kotlinx.coroutines.flow.FlowKt__ReduceKt$first$$inlined$collectWhile$2.emit(Limit.kt:142)
       at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:396)
       at kotlinx.coroutines.flow.FlowKt__ReduceKt.first(Reduce.kt:213)
       at kotlinx.coroutines.flow.FlowKt.first(:1)
       at com.example.ardrawsketch.ui.activity.MainActivity$setupViews$1$1.invokeSuspend(MainActivity.kt:43)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:365)
       at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
       at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
       at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
       at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
       at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
       at kotlinx.coroutines.BuildersKt.launch(:1)
       at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
       at kotlinx.coroutines.BuildersKt.launch$default(:1)
       at com.example.ardrawsketch.ui.activity.MainActivity.setupViews$lambda$0(MainActivity.kt:42)
       at com.example.ardrawsketch.ui.activity.MainActivity.$r8$lambda$1WSZW11IMUxPs7-1lyyEmF1Ssik()
       at com.example.ardrawsketch.ui.activity.MainActivity$$ExternalSyntheticLambda0.run(D8$$SyntheticClass)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8663)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

Here is my code in version that older than 1.7.3 version:

@AndroidEntryPoint
class MainActivity :
    BaseActivity<ActivityMainBinding, ActivityMainViewModel>(R.id.nav_host, R.navigation.nav_main) {

    ...
  override fun setupViews() {
    setStatusBarColor(R.color.white)
    binding.main.post {
      when (loginState) {
        OnBoardingState.FIRST_TIME.value -> {
          if (isLanguageSet) {                             
  47 ->     navHostController.navigate(R.id.action_mainFragment_to_onBoardingFragment)
          } else {
            navHostController.navigate(R.id.action_mainFragment_to_languageFragment)
          }
        }

        OnBoardingState.FINISHED.value -> {
  54 ->   navHostController.navigate(R.id.action_mainFragment_to_dashBoardFragment)
        }
      }
      true
    }
  }
   ...

}

Here is my 1.7.3 version code:

@AndroidEntryPoint
class MainActivity :
    BaseActivity<ActivityMainBinding, ActivityMainViewModel>(R.id.nav_host, R.navigation.nav_main) {

    ...

    override fun setupViews() {
        setStatusBarColor(R.color.white)
        binding.main.post {
            when (loginState) {
                LoginState.FIRST_TIME.value -> {
                    if (isLanguageSet) {
                        safeNavigate(
                            navHostController?.currentDestination?.id,
                            R.id.action_mainFragment_to_onBoardingFragment
                        )
                    } else {
                        safeNavigate(
                            navHostController?.currentDestination?.id,
                            R.id.action_mainFragment_to_languageFragment
                        )
                    }
                }

                LoginState.LOGGED_IN.value -> {
                    safeNavigate(
                        navHostController?.currentDestination?.id,
                        R.id.action_mainFragment_to_dashBoardFragment
                    )
                }
            }
        }
    }

   ...

}
abstract class BaseActivity<Binding : ViewBinding, ViewModel : androidx.lifecycle.ViewModel> :
    AppCompatActivity {

    @IdRes
    private var navId: Int? = null

    @NavigationRes
    private var navResource: Int? = null

    /**
     * The NavController for the activity's navigation graph.
     */
    protected var navHostController: NavController? = null

    /**
     * The ViewBinding for the activity.
     */
    protected lateinit var binding: Binding

    /**
     * This method should return the ViewBinding for the activity.
     */
    protected abstract fun getViewBinding(): Binding

    /**
     * The ViewModel for the activity.
     */
    protected lateinit var viewModel: ViewModel

    /**
     * This method should return the Class object for the ViewModel.
     */
    protected abstract fun getViewModelClass(): Class<ViewModel>

    constructor()

    constructor(@IdRes navId: Int, @NavigationRes navResource: Int) {
        this.navId = navId
        this.navResource = navResource
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = getViewBinding()
        viewModel = ViewModelProvider(this)[getViewModelClass()]
        setContentView(binding.root)
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        init()
    }

    private fun init() {
        setupNav()
        setupViews()
    }

    /**
     * Set up the navigation for the activity.
     */
    private fun setupNav() {
        if (navId != null && navResource != null) {
            val navHostFragment =
                supportFragmentManager.findFragmentById(navId!!) as NavHostFragment
            if (navHostController == null) {
                navHostController = navHostFragment.navController
                navHostController?.setGraph(navResource!!, intent.extras)
            }
        }
    }

    /**
     * This method should set up the views for the activity.
     */
    abstract fun setupViews()

    protected fun safeNavigate(
        currentDestination: Int?,
        action: Int,
        bundle: Bundle? = null
    ) {
        if (navHostController?.currentDestination?.id == currentDestination) {
            lifecycle.addObserver(object : LifecycleEventObserver {
                override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                    if (event == Lifecycle.Event.ON_RESUME) {
                        lifecycle.removeObserver(this)
                        try {
                            navHostController?.navigate(action, bundle)
                        } catch (e: IllegalArgumentException) {
                            e.printStackTrace()
                        }
                    }
                }
            })
        }
    }

    protected fun safeNavigate(
        currentDestination: Int?,
        action: NavDirections
    ) {
        if (navHostController?.currentDestination?.id == currentDestination) {
            lifecycle.addObserver(object : LifecycleEventObserver {
                override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                    if (event == Lifecycle.Event.ON_RESUME) {
                        lifecycle.removeObserver(this)
                        try {
                            navHostController?.navigate(action)
                        } catch (e: IllegalArgumentException) {
                            e.printStackTrace()
                        }
                    }
                }
            })
        }
    }
}

I tried to catch the IllegalArgumentException error but it's still logged in the crashlytics of Firebase:

 protected fun safeNavigate(
        currentDestination: Int?,
        action: NavDirections
    ) {
        if (navHostController?.currentDestination?.id == currentDestination) {
            lifecycle.addObserver(object : LifecycleEventObserver {
                override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                    if (event == Lifecycle.Event.ON_RESUME) {
                        lifecycle.removeObserver(this)
                        try {
                            navHostController?.navigate(action)
                        } catch (e: IllegalArgumentException) {
                            e.printStackTrace()
                        }
                    }
                }
            })
        }
    }

Solution

  • The message clearly says what the issue is. For some reason the triggered destination doesn't exists at runtime. These might be the causes of crash: