Skip to content

[$250] Android: Tapping push notification on cold start opens last active chat instead of the notification's chat #92138

@melvin-bot

Description

@melvin-bot

Description

When the app is cold-started by tapping a push notification (i.e. Android had killed the process while idle), the app navigates to the last active chat rather than the chat referenced in the notification. The correct chat is visible if you press the back button from that screen, meaning the navigation did eventually happen — but underneath the wrong screen.

Reported by dbarrett@expensify.com on Android staging, 2026-05-29 ~13:59 UTC. Tapped a notification from Florent, but the app opened to Chronos (last active chat). Pressing back from Chronos revealed the Florent chat.

Root Cause (confirmed via VictoriaLogs)

This is a cold-start race condition introduced by the dynamic import added in #84624.

subscribeToPushNotifications is now lazily imported inside a useEffect gated on splashScreenState === HIDDEN:

useEffect(() => {
  if (splashScreenState !== HIDDEN) return;
  import('.../subscribeToPushNotifications');
}, [splashScreenState]);

When the user taps a notification to cold-launch the app, the Airship native bridge fires notification_response before the splash screen hides and before PushNotification.onSelected(REPORT_COMMENT, navigateToReport) has been registered. The tap event is dispatched into the void — navigateToReport is never called.

Log Evidence

At 13:59:38.491Z the device correctly emitted:

[PushNotification] Callback triggered for com.airship.notification_response

But none of the subsequent JS-side logs appeared:

  • [PushNotification] Navigating to report
  • [PushNotification] onSelected() - Navigation is ready. Navigating...
  • [PushNotification] Not navigating because this is a singleNewDotEntry flow
  • [PushNotification] onSelected() - failed

Since [PushNotification] Navigating to report is the first line of navigateToReport (before any await or guard), its absence proves the JS handler was never invoked — not just delayed.

Two earlier taps the same day (00:48 UTC and 03:29 UTC, when the app was warm) worked correctly, confirming the regression is specific to cold starts.

Two corroborating signals for cold start:

  1. Previous push activity was at 12:44 UTC — 75 minutes of silence before the broken tap, enough for Android to kill the process.
  2. A new staging build (ecash9.3.89-2) appeared between the broken tap and the next session, suggesting the 13:59 launch may also have been triggered by an app update.

Steps to Reproduce

  1. Open the app on Android, navigate to any chat, then background the app and leave it idle for ~1 hour (or force-kill it).
  2. Receive a push notification for a different chat.
  3. Tap the notification.

Expected: App opens directly to the notification's chat.
Actual: App opens to the last active chat. The notification's chat is reachable via the back button.

Relevant Code

Platform

  • Platform: Android
  • Environment: Staging (likely also affects production)
  • App version at time of report: ecash9.3.83-1
Upwork Automation - Do Not Edit

Metadata

Metadata

Assignees

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorHelp WantedApply this label when an issue is open to proposals by contributorsHot PickReady for an engineer to pick up and run with

Type

No type
No fields configured for issues without a type.

Projects

Status

CRITICAL

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions