Skip to content

Fix preventBackNavigation crash by clearing the pending activity result - #23150

Closed
nbradbury wants to merge 1 commit into
trunkfrom
issue/prevent-back-navigation-result-crash
Closed

Fix preventBackNavigation crash by clearing the pending activity result#23150
nbradbury wants to merge 1 commit into
trunkfrom
issue/prevent-back-navigation-result-crash

Conversation

@nbradbury

@nbradbury nbradbury commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Problem

Activity.finishAffinity() throws IllegalStateException("Can not be called to deliver a result") when the activity's result code isn't RESULT_CANCELED or it has result data. openPlayStoreLink() is called from fragments hosted by a wide range of activities, several of which set a result — so the host can have one pending.

#22838 tried to fix this by guarding on isFinishing/isDestroyed. That's orthogonal to the precondition AOSP actually checks, so it was a no-op: the crash continued through 26.8 and 26.9 and is still present in 27.0 (~239 users across the three Sentry groups). This clears the result before finishing instead, plus a finish() fallback for the remaining embedded-activity throw path.

Sentry: WORDPRESS-ANDROID-3F23, WORDPRESS-ANDROID-3F1V, WORDPRESS-ANDROID-3F3X

Verified end to end

Reproduced and fixed on a Pixel 8 / API 35 emulator. Two temporary local edits were needed to reach the code path (neither is in this PR): pointing JETPACK_PACKAGE_NAME at com.jetpack.android.prealpha so a debug Jetpack install counts as installed, and adding requireActivity().setResult(RESULT_OK, Intent()) in JetpackStaticPosterFragment.onViewCreated to give the host a pending result.

Before the fix — WordPress app → Reader tab (JetpackStaticPoster) → "Switch to the Jetpack app":

java.lang.IllegalStateException: Can not be called to deliver a result
	at android.app.Activity.finishAffinity(Activity.java:7350)
	at org.wordpress.android.ui.ActivityLauncherWrapper.preventBackNavigation(ActivityLauncherWrapper.kt:81)
	at org.wordpress.android.ui.ActivityLauncherWrapper.openPlayStoreLink(ActivityLauncherWrapper.kt:53)
	at ...JetpackStaticPosterFragment.observeEvents$lambda$0(JetpackStaticPosterFragment.kt:64)

Same frames as WORDPRESS-ANDROID-3F23 (line numbers differ by the 3 added lines; Activity.java:7350 vs 7150 is the API level).

After the fix, same steps: 0 fatal exceptions, Jetpack in the foreground, 0 remaining WordPress activity records (the task was finished), and Back goes to the launcher rather than WordPress — so the feature's intent is preserved. The catch fallback did not fire, meaning finishAffinity() itself succeeded rather than being rescued.

Still open

  1. Side effect. Clearing the result is a behavior change. finishAffinity() only finishes activities sharing this affinity, so an activity in a different task that started this one for a result now receives RESULT_CANCELED instead of whatever was pending. That looks right for a "hand the user to Jetpack and tear down the task" flow, but I haven't proven no caller depends on it. This is the part worth reviewing.
  2. No automated regression test. The unit tests mock Activity, so finishAffinity() is a stub and they cannot exercise this precondition — exactly why Fix Sentry WORDPRESS-ANDROID-3F23: guard preventBackNavigation #22838 shipped green. A real-Activity instrumented test would catch it, but androidTest doesn't compile on trunk in three independent pre-existing places (MySitesPage.kt Matchers.isA<Any>(...) type mismatch, MySitesPage.kt unresolved R.id.quick_link_item, BaseTest.java:49 createComposeRule signature). I left that test out rather than ship code I couldn't compile.
  3. The production trigger is still unidentified. My repro manufactures the pending result. Every setResult I found on a reachable host is immediately followed by finish(), which the existing isFinishing guard already short-circuits; the only long-lived one is ReaderPostListFragment.onAttach(), which can't run in the WordPress app because the poster replaces the Reader. So something sets a result on these hosts by a path I haven't found. The fix is unconditional and doesn't depend on knowing it, but it means we can't yet write a test that fails for the real reason.

Testing

./gradlew :WordPress:testWordPressDebugUnitTest --tests "*ActivityLauncherWrapperTest*" — 8/8 pass. The two new cases assert setResult(RESULT_CANCELED, null) runs before finishAffinity(), and that an IllegalStateException from finishAffinity() falls back to finish() instead of propagating.

Follow-ups

  • androidTest is broken on trunk (see above) — worth fixing independently.
  • ReaderPostListFragment.onAttach() sets RESULT_OK unconditionally on its host, whether or not anything changed.

Activity.finishAffinity() throws IllegalStateException("Can not be called to
deliver a result") when the activity's result code isn't RESULT_CANCELED or it
has result data. openPlayStoreLink() is called from fragments hosted by a wide
range of activities, several of which set a result, so the host frequently has
one pending.

The earlier guard added in #22838 checked isFinishing/isDestroyed, which is
orthogonal to that precondition, so it never suppressed the crash. Clear the
result before finishing instead — we're tearing down the whole task to hand the
user over to Jetpack, so the activity that would have received it is being
finished too. Falls back to finish() for the remaining embedded-activity path.

Sentry: WORDPRESS-ANDROID-3F23, WORDPRESS-ANDROID-3F1V, WORDPRESS-ANDROID-3F3X
@dangermattic

Copy link
Copy Markdown
Collaborator
1 Message
📖 This PR is still a Draft: some checks will be skipped.

Generated by 🚫 Danger

@wpmobilebot

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
Build TypeDebug
Versionpr23150-24cda26
Build Number1498
Application IDcom.jetpack.android.prealpha
Commit24cda26
Installation URL2nkfvl8bco9t0
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
Build TypeDebug
Versionpr23150-24cda26
Build Number1498
Application IDorg.wordpress.android.prealpha
Commit24cda26
Installation URL4q73ge4qm75gg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 37.84%. Comparing base (3ab17e4) to head (24cda26).

Additional details and impacted files
@@           Coverage Diff           @@
##            trunk   #23150   +/-   ##
=======================================
  Coverage   37.84%   37.84%           
=======================================
  Files        2345     2345           
  Lines      127511   127514    +3     
  Branches    17709    17709           
=======================================
+ Hits        48255    48258    +3     
  Misses      75298    75298           
  Partials     3958     3958           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nbradbury

Copy link
Copy Markdown
Contributor Author

Closing this as I'm not convinced Claude found the real problem.

@nbradbury nbradbury closed this Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants