Skip to content

Fix: Nav3 "Key used multiple times" crash (b/516312097)#3014

Merged
StylianosGakis merged 13 commits into
developfrom
fix/nav3-duplicate-key-crash
Jul 2, 2026
Merged

Fix: Nav3 "Key used multiple times" crash (b/516312097)#3014
StylianosGakis merged 13 commits into
developfrom
fix/nav3-duplicate-key-crash

Conversation

@StylianosGakis

Copy link
Copy Markdown
Member

Production crash java.lang.IllegalArgumentException: Key HomeKey was used multiple times, thrown by SaveableStateProvider when NavDisplay composes two live entries that share a contentKey. This is the Nav3 framework bug b/516312097.

AI Summary:

Root cause

Two independent contributors, both fixed here:

  1. Unstable custom-scene equality. NavEntry.equals compares its content composable lambda by reference identity. Any custom Scene whose equality flows through NavEntry value-equality therefore looks like a brand-new scene whenever that lambda is recreated during a transition, and its contentKey gets registered twice.
  2. An auth-driven re-root flap. AuthTokenServiceImpl.authStatus reported LoggedIn purely on token presence. A stored session with an already-expired refresh token would, on cold start, root the app at Home and then get bounced back to Login once the first request's refresh failed, a rapid Login -> Home -> Login re-root that stressed the duplicate-key path.

Changes

  • BottomSheetScene now compares equals/hashCode by contentKey instead of by NavEntry identity.
  • NavSuiteScene / NavUpBarScene converted from data class to regular classes with explicit key-based equals/hashCode (their key is scene::class to scene.key, already a stable value). NavSuiteScene uses identical logic to NavUpBarScene.
  • AuthTokenServiceImpl takes an injected Clock and emits LoggedOut when the stored refresh token is expired (exact expiry), so an unrecoverable session no longer flashes as logged in. The login flow is unaffected: a fresh login writes a future-dated refresh token.
  • navigation3 bumped 1.2.0-alpha03 -> 1.2.0-alpha04, which fixes the framework side (comparing OverlayScenes by scene key).

…ability

Use capturing lambdas in the regression test so each buildScene() call
produces NavEntry instances with distinct content lambda references. The
old empty-lambda form was compiled to a shared singleton, causing both
entries to share one content reference and the identity-based equals to
return true even before the contentKey fix, making the test vacuous.
…2097)

Convert NavSuiteScene and NavUpBarScene from private data class to internal
class, replacing the auto-generated data class equals/hashCode (which delegated
to the wrapped Scene and therefore to NavEntry.content lambda identity) with
explicit key-based equality: two wrappers are equal iff scene::class and scene.key
match. Adds navUpBarSceneDecoratorStrategy() as a public factory for the ShowUpBar
path that does not need a SharedTransitionScope, enabling plain-JUnit testing.
NavUpBarSceneEqualityTest verifies the fix RED/GREEN through that factory; NavSuiteScene
is covered by the identical equals/hashCode logic (SharedTransitionScope makes direct
construction in a plain JVM test impractical, as noted in its class comment).
navUpBarSceneDecoratorStrategy has no production caller: it exists purely
so NavUpBarSceneEqualityTest in :app can reach the internal NavUpBarScene
equality logic. Annotate it @VisibleForTesting and replace the KDoc that
falsely claimed "callers that only need the up-bar decoration can use it"
with an honest description of its role as a test seam.
@StylianosGakis StylianosGakis requested a review from a team as a code owner July 2, 2026 15:15
@StylianosGakis StylianosGakis enabled auto-merge July 2, 2026 15:15
@StylianosGakis StylianosGakis merged commit d42b2c4 into develop Jul 2, 2026
4 checks passed
@StylianosGakis StylianosGakis deleted the fix/nav3-duplicate-key-crash branch July 2, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant