Skip to content

[$250] App stuck displaying WS chat pg in loop on tapping back button after deleting reports #82373

Closed

Description

@jponikarchuk

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.3.18-2
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: Exploratory around #81741
Email or phone of affected tester (no customers): kiatest.80+ck2122612@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: iPhone 16/iOS 18.7.1, Pixel 8a/Android 16
App Component: Money Requests

Action Performed:

  1. Open Hybrid app
  2. Join as new user with gmail account (Select Somthing else during onboarding)
  3. Go to Self DM & create a manual expense
  4. Open the expense >> Tap More >> Duplicate option
  5. Go to Reports
    6.Open the second expense
  6. Select Report field >> Upgrade >> Confirm >> Got it, thanks
  7. Tap create new report
  8. Open the second expense
  9. Tap From link
  10. Tap + icon >> create new report (This is an optional step)
  11. Delete both report(s) via contextual menu (or delete it from expense details page)
  12. Tap back button & observe workspace chat page is displayed
  13. Repeat step 13 few more times & observe workspace chat page displayed in loop
  14. Swipe page to right using swipe gesture - observe not here page appears & return to WS chat page

Expected Result:

App should not stuck in workspace chat page & user should be redirected to Inbox after deleting reports

Actual Result:

App get stuck displaying same workspace chat page in loop on tapping back button after deleting reports

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari

Screenshots/Videos

1.mp4

View all open jobs on GitHub

Issue OwnerCurrent Issue Owner: @aimane-chnaif
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~022028954394778650882
  • Upwork Job ID: 2028954394778650882
  • Last Price Increase: 2026-03-24

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Mar 3, 2026
  2. melvin-bot commented on Mar 3, 2026

    @melvin-bot

    Triggered auto assignment to @CortneyOfstad (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

  3. MelvinBot commented on Mar 3, 2026

    @MelvinBot
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    After deleting expense reports in a workspace, pressing the back button gets stuck in a loop displaying the same workspace chat page repeatedly. The user cannot escape to the Inbox. Swiping right briefly shows a "not here" page before returning to the workspace chat.

    What is the root cause of that problem?

    When expense reports are deleted, only the Onyx data is cleared — the navigation stack inside the ReportsSplitNavigator is not cleaned up. The orphaned report screen entries remain in the stack.

    When the user presses back and lands on one of these stale entries, ReportScreen detects the missing report data and redirects to the parent workspace chat using Navigation.navigate(). Critically, Navigation.navigate() is converted to a PUSH action by linkTo when the target reportID differs from the currently focused one. This adds a new workspace chat entry on top of the stack instead of replacing the stale entry.

    The result: pressing back pops the new workspace chat entry, revealing the next stale deleted report underneath, which triggers another redirect+push, creating an infinite loop.

    Specifically, two independent useEffect hooks in ReportScreen.tsx both detect deletion and redirect via Navigation.navigate():

    1. Lines 700-768 — detects isRemovalExpectedForReportType (covers isMoneyRequestReport), calls Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(prevReport.parentReportID)) at line 759
    2. Lines 797-819 — uses useReportWasDeleted hook, calls Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(deletedReportParentID)) at line 810

    Both push new entries rather than cleaning up the stale ones.

    A Navigation.removeReportScreen() function exists at Navigation.ts:815-831 that can filter report screens from the navigation state, but it is only called from useSelectedTransactionsActions.ts:150 (bulk transaction delete in search) — it is never called during the regular report deletion flow (deleteAppReport / deleteReport).

    What changes do you think we should make in order to solve the problem?

    The redirect navigation in ReportScreen when a report is detected as deleted should not push a new entry. Instead, it should clean up the stale navigation entries. Two approaches:

    1. Call Navigation.removeReportScreen() for the deleted reportID(s) in the deletion flow (inside deleteAppReport or in the ReportScreen useEffect before navigating). This would remove the orphaned entries from the SplitNavigator stack. Note: removeReportScreen currently dispatches against the root navigator state and may need to be updated to reach into the nested SplitNavigator state.

    2. Use REPLACE instead of PUSH for the redirect navigation in ReportScreen when handling deleted reports. The Navigation.navigate() calls at lines 759 and 810 could pass a flag (e.g., forceReplace: true) so linkTo uses REPLACE instead of converting to PUSH. This prevents the stack from growing but doesn't remove the stale entries underneath — it would still require multiple back presses to escape if many reports were deleted.

    Recommended: Option 1 is the more complete fix. When a report is deleted and ReportScreen detects it, call Navigation.removeReportScreen(new Set([reportIDFromRoute])) before navigating to the parent. This mirrors what useSelectedTransactionsActions already does for bulk deletes.

    What alternative solutions did you explore? (Optional)

    • Calling popToSidebar() unconditionally before redirecting: This would work but is too aggressive — it clears the entire stack including legitimate entries the user may want to navigate back to.
    • Adding cleanup to deleteReport() / deleteAppReport(): This would require passing navigation context into data-layer functions, which violates the current separation of concerns. The cleanup is better placed in ReportScreen where the stale state is detected.
    Detailed Investigation

    Root Cause Analysis

    The navigation architecture uses a ReportsSplitNavigator with a stack: [INBOX, REPORT(chat), REPORT(expense1), REPORT(expense2), ...]. When expense reports are deleted:

    1. deleteReport() at src/libs/actions/Report/index.ts:3688-3725 nullifies Onyx data but does nothing to the navigation state
    2. deleteAppReport() at src/libs/actions/Report/index.ts:5400+ marks reports as pending deletion optimistically, then nulls on API success — also no nav cleanup
    3. The stale REPORT entries remain in the SplitNavigator stack

    When ReportScreen renders for a stale entry:

    • useReportWasDeleted (at src/pages/inbox/hooks/useReportWasDeleted.ts) detects the report data is gone
    • The useEffect at line 797 fires and calls Navigation.navigate() to the parent (workspace chat)
    • linkTo at src/libs/Navigation/helpers/linkTo/index.ts:154-161 converts this to PUSH because the target reportID differs from the current focused route
    • A new workspace chat screen is pushed on top of the stale entry

    Evidence Chain

    1. User navigates: [INBOX] → [REPORT(ws-chat)] → [REPORT(expense1)] → [REPORT(expense2)]
    2. Reports deleted: Onyx data cleared, nav stack unchanged
    3. Focused ReportScreen (expense2) detects deletion → pushes REPORT(ws-chat) → stack: [INBOX, REPORT(ws-chat), REPORT(expense1-stale), REPORT(expense2-stale), REPORT(ws-chat-new)]
    4. Back button: pops ws-chat-new → lands on expense2-stale → redirect fires → pushes ws-chat again
    5. Infinite loop

    Relevant Code

    • src/pages/inbox/ReportScreen.tsx:700-768 — Report removal detection and redirect
    • src/pages/inbox/ReportScreen.tsx:797-819 — useReportWasDeleted redirect
    • src/libs/Navigation/helpers/linkTo/index.ts:154-161 — NAVIGATE→PUSH conversion
    • src/libs/Navigation/Navigation.ts:815-831 — removeReportScreen (exists but unused in this flow)
    • src/libs/actions/Report/index.ts:3688-3725 — deleteReport (Onyx-only, no nav cleanup)
    • src/hooks/useSelectedTransactionsActions.ts:150 — Only place removeReportScreen is called

    Implementation Notes

    • removeReportScreen dispatches against the root navigator state but report screens are nested inside ReportsSplitNavigator. The function may need to be updated to traverse into nested navigator states.
    • Both redirect effects (lines 700-768 and 797-819) could potentially fire for the same deletion event, creating double navigation entries. The fix should handle this by cleaning up before either redirect fires.
    • The isFocused guard prevents non-focused stale screens from redirecting immediately, but they become focused when the user navigates back to them.

    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, or @MelvinBot <your feedback> to refine this analysis.

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Mar 3, 2026
  5. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Mar 3, 2026
  6. melvin-bot commented on Mar 3, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @aimane-chnaif (External)

  7. changed the title [-]App stuck displaying WS chat pg in loop on tapping back button after deleting reports[/-] [+][$250] App stuck displaying WS chat pg in loop on tapping back button after deleting reports[/+] on Mar 3, 2026
  8. melvin-bot commented on Mar 3, 2026

    @melvin-bot
  9. melvin-bot commented on Mar 9, 2026

    @melvin-bot

    @aimane-chnaif Eep! 4 days overdue now. Issues have feelings too...

  10. 38 remaining items

  11. m-natarajan commented on Apr 8, 2026

    @m-natarajan

    Not reproducible on build v9.3.54-3 on iPhone 16/18.7.1

    ScreenRecording_04-08-2026.12-51-27_1.MP4
  12. aimane-chnaif commented on Apr 8, 2026

    @aimane-chnaif
    Contributor

    @MelvinBot which recent PR fixed this?

  13. MelvinBot commented on Apr 8, 2026

    @MelvinBot
    Contributor

    There's no single PR that directly targeted this issue, but several related PRs merged between v9.3.18 and v9.3.54 likely fixed it collectively. The most relevant candidates:

    1. fix: filter deleted reports from navigation and disable in search list #85292 — "Filter deleted reports from navigation and disable in search list" (TaduJR, merged Mar 25). Adds isReportPendingDelete filtering so deleted reports are excluded from navigation sections, preventing the infinite-loading/redirect loop when landing on a deleted report.

    2. Fix Not Here page after removing split expense #84656 — "Fix Not Here page after removing split expense" (MelvinBot, merged Mar 13). Fixes navigation to a deleted expense report after removing splits.

    3. Fix 'Not Found' page flash after deleting expense #83613 — "Fix 'Not Found' page flash after deleting expense" (MelvinBot, merged Mar 2). Defers deleteTransaction() until after the RHP close animation to prevent stale navigation state.

    4. Use backward navigation pattern for reverse-split deletion #85425 — "Use backward navigation pattern for reverse-split deletion" (MelvinBot, merged Mar 24). Fixes back navigation after reverse-split report deletion.

    5. Navigate to Home instead of Inbox from NotFoundPage #85526 — "Navigate to Home instead of Inbox from NotFoundPage" (merged Mar 19). Changes where not-found pages redirect to.

    Note: QA also reported that step 4 of the reproduction (duplicating a tracked expense) itself fails on v9.3.54-2, so the original reproduction path may no longer be valid regardless of the navigation fix.

  14. melvin-bot commented on Apr 13, 2026

    @melvin-bot

    @aimane-chnaif Huh... This is 4 days overdue. Who can take care of this?

  15. CortneyOfstad commented on Apr 14, 2026

    @CortneyOfstad
    Contributor

    @aimane-chnaif any update based on Melvin's response above?

  16. aimane-chnaif commented on Apr 14, 2026

    @aimane-chnaif
    Contributor

    @CortneyOfstad let's close this as not reproducible

  17. melvin-bot commented on Apr 17, 2026

    @melvin-bot

    @aimane-chnaif Whoops! This issue is 2 days overdue. Let's get this updated quick!

  18. mvtglobally commented on Apr 18, 2026

    @mvtglobally

    Issue not reproducible during KI retests. (First week)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorretest-weeklyApply this label if you want this issue tested on a Weekly basis by Applause

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions