Skip to content

A quick Back no longer crashes, and the crash report screen survives a crash - #113

Merged
StaticHumStudio merged 3 commits into
masterfrom
fix/220g-back-crash
Nov 2, 2026
Merged

StaticHumStudio merged 3 commits into
masterfrom
fix/220g-back-crash

Conversation

@StaticHumStudio

Copy link
Copy Markdown
Owner

Fixes #104.

What changes for the listener

A quick Back while the app changes pages no longer crashes (#104). In navigation-compose 2.8.5, a Back (key or swipe) is queued to the next main thread turn and reads the page list as of the last draw. If the app changes pages in that gap (the book page's back arrow, Play opening the Player, a tab tap), the queued work animates a page that's already gone and throws "Cannot transition entry that is not in the back stack". BackRaceGuard cancels any Back the host hasn't acted on yet whenever the destination changes, so the stale work never runs. Upgrading to navigation 2.10 is the real upstream fix, but it needs AGP 9.1 and compileSdk 37, so that waits. 2.9.x still has the race.

The crash report screen survives a crash again. This is a 2.2.0 regression from the widget work (#110, #111). NineLivesApp.onCreate ran all main process startup inside ACRA's :acra crash dialog process, and the widget refresher's WorkManager call threw there and took the dialog down. Startup now runs only in the main process, and the injected fields are lazy so the crash process never builds them.

Choices worth knowing

  • The guard calls dispatchOnBackCancelled(), which is public but marked @VisibleForTesting. The version catalog now says BackGestureRaceTest must pass on a device before activity or navigation gets bumped.
  • A Back that lands in the same instant as an app page change gets dropped, because the page change already moved the user. A swipe that's mid gesture when the app pops then pops once more on release (pinned by test, lands on Home from Book Detail).

Evidence

  • JVM: 2,003 tests, 0 failures. 5 new process tests with a deliberate failure canary.
  • Instrumented BackGestureRaceTest: 7 of 7 pass. Without the guard, 3 fail with the production stack.
  • Emulator hammer: master crashed once in 145 rounds, this branch ran 344 rounds with 0 crashes.
  • ACRA forced crash: before, the dialog's process crashed 1.2 s later on "WorkManager is not initialized". After, the dialog stays up with 0 errors.
  • One independent Opus review: nothing blocking, two test hardening items applied.
  • Evidence: nine-lives-evidence/2026-11-02-220g-104/RESULTS.md

🤖 Generated with Claude Code

https://claude.ai/code/session_014XfNSbF1fe9aiDwGMCNLmj

StaticHumStudio and others added 3 commits November 2, 2026 16:40
The navigation host in navigation-compose 2.8 does not act on Back right
away. It queues the work for the next turn of the main thread and then
animates the page it last drew on top. If the app changed the back stack
in that gap (Play finishing its load and opening the Player, the book
page's back arrow, a tab tap) the page was already gone and the host threw
"Cannot transition entry that is not in the back stack", killing the app.

Now every back stack change, from any caller, cancels a Back the host has
not acted on yet. The change that cancelled it already moved the user, so
nothing they asked for is lost.

Navigation 2.10 reworked this path, but it needs Android Gradle plugin 9.1
and compileSdk 37, so the app-side guard is the fix for now.

Instrumented tests reproduce the race on a real activity: the three race
cases throw the production stack trace without the guard and pass with it,
and plain Back and Back swipe still land on the Library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014XfNSbF1fe9aiDwGMCNLmj
Android runs the app's startup code in every process the app has,
including the crash dialog's own ":acra" process. Since the 2.2.0 widget
work, that startup code schedules widget work through WorkManager, which
only exists in the main process. So after any crash the dialog's process
threw "WorkManager is not initialized properly" and the crash report
dialog died before the user could send anything.

Startup now checks which process it is in and stops right after ACRA in
any process other than the main one. Billing, sync, connectivity, the
download cleanup, playback restore and the widget refresher all assumed
the main process, so none of them start in the crash process now. The
app's dependencies are injected lazily, so the crash process does not
even build them.

AppProcessTest pins the process check: the main process counts, the
crash process and any other side process do not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014XfNSbF1fe9aiDwGMCNLmj
…104)

Review follow ups for the back race guard.

BackGestureRaceTest now checks that a plain Back and a plain Back swipe
finish the page change, not just pick the right route: the Library is
RESUMED, it is the only page the host still draws, and the book page was
torn down. The swipe race test now asserts where the user ends up, and a
new variant pops the page while the swipe is already in progress.

BackRaceGuard explains that its listener also fires at registration and
after a configuration change with nothing pending, which is harmless. The
version catalog marks activityCompose and navigationCompose bumps as gated
on BackGestureRaceTest, because the guard relies on the test-only
dispatchOnBackCancelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014XfNSbF1fe9aiDwGMCNLmj
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T22:56:52.402583Z d53b9c2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@StaticHumStudio
StaticHumStudio merged commit 85ab31e into master Nov 2, 2026
1 check passed
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.

A quick back gesture can crash navigation with "Cannot transition entry that is not in the back stack"

1 participant