Skip to content

[Exploratory] Workspace - App crashes when reopening workspace settings link #98300

Open

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


Version Number: 9.4.52-0
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/test-runs/68133792/folder/53676493/46436410/1639586868?sort=-identifier
Email or phone of affected tester (no customers): sdsiodjnosdjiodsj@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Mac 26.5 / Chrome
App Component: Workspace Settings

Action Performed:

  1. Go to staging.new.expensify.com
  2. Create a workspace.
  3. Go to workspace settings > Overview.
  4. Click workspace name.
  5. Copy URL.
  6. Go to Inbox and send the URL to any chat.
  7. Click on the sent link.
  8. Close workspace name RHP.
  9. Click app back button on the top left.
  10. Go to Inbox.
  11. Open the same chat from Step 6 and click on the link.

Expected Result:

App will not crash.

Actual Result:

App crashes when reopening workspace settings link.

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7230735_1786424586788!staging.new.expensify.com-1786424442508.txt

Bug7230735_1786424586790.25.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Mita Rachim
Bug ID: 7230735

Issue OwnerCurrent Issue Owner: @mananjadhav

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 5, 2026
  2. MelvinBot commented on Sep 5, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    The console log attached to this issue names the crash exactly:

    TypeError: Cannot read properties of undefined (reading 'backTo')
        at WorkspaceOverviewPage
    

    src/pages/workspace/WorkspaceOverviewPage.tsx:97-98 reads route.params.backTo and route.params.policyID with no guard. On the second open of the link, route.params is undefined, so the component throws on its first statement — before any render guard can run.

    route.params becomes undefined because WORKSPACE.PROFILE is the defaultCentralScreen of the WorkspaceSplitNavigator (WorkspaceSplitNavigator.tsx:57). When the split navigator is re-entered without its nested state, the router falls back to an initial state holding only the sidebar route, whose params are deliberately undefined. SplitRouter.ts:87-90 then injects the central screen with params: state.routes.at(0)?.params — copying that undefined straight onto WORKSPACE.PROFILE.

    The first click works and the second crashes because the two clicks take different branches in linkTo: the first sees an unvisited Workspaces tab and dispatches the full nested state; the repeat visit does not.

    TypeScript does not catch this. types.ts:2901-2905 types WORKSPACE.PROFILE params as {policyID: string; backTo?: Routes} without | undefined, which makes React Navigation's conditional Route type mark params as required. The type does not match runtime.

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

    1. Guard the crashing reads in WorkspaceOverviewPage.tsx — use optional chaining for backTo and policyID at lines 97-98 (and the same reads at lines 349 and 769), and skip the policy fetch when policyID is missing. This is exactly the pattern already applied to the sibling sidebar screen in WorkspaceInitialPage.tsx:86 by Fix workspace navigation after distance settings deep link #98423, and the wrapper this page already renders through, withPolicy.tsx:79, optional-chains the same route object.

    2. Fix the type so this class of bug is visible. Widen the split-navigator param list entry so params is optional for WORKSPACE.PROFILE. Without this, adding ?. looks like dead code to a reviewer and will get removed again.

    3. Preserve the policyID at the router level so the central screen is not synthesised paramless — SplitRouter.ts:87-90 should carry the sidebar's policyID rather than copying a possibly-undefined params object.

    Step 1 stops the crash. Steps 2 and 3 matter because 20 of the 22 screens registered in WorkspaceSplitNavigator read route.params unguarded — only WorkspaceInitialPage and PolicyAccountingPage are defensive. A page-local fix leaves the rest as latent crash sites.

    What alternative solutions did you explore? (Optional)

    Alternatives considered, evidence, and verification notes

    Alternative: treat this as already fixed. #98423 (merged 2026-08-31) repaired the linkTo path that dropped the split navigator's nested state — the same mechanism described above, and its PR body describes the identical /workspaces/undefined symptom. This issue was reported on 2026-08-11 against 9.4.52-0, which predates that fix. I could not rule this out as the reason: the automated web test run for this analysis failed on an infrastructure problem (the browser session never rendered), so I have no live staging confirmation either way. Even if #98423 removed this particular trigger, the unguarded read at line 97 and the incorrect type are still on main today, so the crash remains one navigation change away.

    Alternative: an error boundary or a not-found page. That converts a crash into a broken screen without fixing the missing policyID, and #69850 already tracks making error pages recoverable. Not a substitute for the guard.

    Alternative: a useEffect guard. Does not work here — the throw happens in the component body at line 97, before any effect runs.

    Supporting evidence that params: undefined is a real runtime state: a merged test, tests/ui/WorkspaceInitialPageTest.tsx:96-101, explicitly mocks {route: {params: undefined}} for a screen in this navigator.

    Git history: the unguarded const backTo = route.params.backTo; was introduced in commit 113d584b (#56204, 2025-01-16) in the then-named WorkspaceProfilePage.tsx and carried through the rename in #56920. It was never optional-chained, so no guard was removed. route.params.policyID at line 98 was added by #89414 (2026-05-08) to satisfy the no-default-id-values lint rule, which replaced a nullish-coalesced read with a raw one.

    Related issues: a quick scan of open App issues found no duplicate. #69850 (error pages should be recoverable) is loosely related but not the same bug.


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/98300) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run · no recording available

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Sep 5, 2026
  4. melvin-bot commented on Sep 5, 2026

    @melvin-bot

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

  5. melvin-bot commented on Sep 5, 2026

    @melvin-bot

    Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.

  6. mananjadhav commented on Sep 6, 2026

    @mananjadhav
    Collaborator

    Reviewing.

  7. melvin-bot commented on Sep 8, 2026

    @melvin-bot

    @mananjadhav this issue is now 4 weeks old, please consider:

    • Finding a contributor to fix the bug
    • Closing the issue if BZ has been unable to add the issue to a VIP or Wave project
    • If you have any questions, don't hesitate to start a discussion in #expensify-open-source

    Thanks!

  8. 11 remaining items

  9. mananjadhav commented on Sep 22, 2026

    @mananjadhav
    Collaborator

    @MelvinBot implement this

  10. MelvinBot commented on Sep 22, 2026

    @MelvinBot
    Contributor

    ⏱️ Ran out of time before I could finish. This usually means the task was too complex or I got stuck exploring. Please try again with a more specific request or post in #melvinbot-ai.

    Run details

  11. melvin-bot commented on Sep 25, 2026

    @melvin-bot

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

  12. melvin-bot commented on Sep 29, 2026

    @melvin-bot

    @mananjadhav 6 days overdue. This is scarier than being forced to listen to Vogon poetry!

  13. melvin-bot commented on Oct 1, 2026

    @melvin-bot

    @mananjadhav Now this issue is 8 days overdue. Are you sure this should be a Daily? Feel free to change it!

  14. melvin-bot commented on Oct 5, 2026

    @melvin-bot

    @mananjadhav 12 days overdue now... This issue's end is nigh!

  15. melvin-bot commented on Oct 8, 2026

    @melvin-bot

    This issue has not been updated in over 14 days. @mananjadhav eroding to Weekly issue.

  16. mananjadhav commented on Oct 23, 2026

    @mananjadhav
    Collaborator

    @MelvinBot Check this comment again #98300 (comment) and see if we have the same update and fix.

  17. MelvinBot commented on Oct 23, 2026

    @MelvinBot
    Contributor

    mananjadhav — yes. On current main the root cause is the same and the fix still works. No merged or open PR has fixed this. Two parts of the proposal need edits: item 1's code moved to a new file, and the item 5 test-harness change has already landed. All links use SHA de7716f.

    Still broken on main:

    What changed in each item:

    # Status What to change
    1 ⚠️ Moved [NoQA] Fix import/no-cycle - part 16 moved the code byte-for-byte into getMatchingFullScreenRoute.ts:157-166 (workspace) and :190-199 (domain). Neither call passes splitNavigatorParams yet. linkTo and swapBackgroundTabForRHPTarget call this function too, so the fix covers them as well.
    2 ✅ Same getInitialSplitNavigatorState.ts:14 is still Record<string, string>. types.ts was split up, so the split-navigator entries are now at types.ts:1322-1323.
    3 ✅ Same All three SplitRouter backfill sites are still needed. Lines shifted about +4: the sidebar is at 93-106 and the central screen at 120-123. Two PRs touched the file since, #101905 and #103746, but neither changed params.
    4 ✅ Same, plus 2 lines fetchPolicyData still has no !routePolicyID guard. Also add ?. at line 407 (clearInviteDraft) and line 951 (RULES_CUSTOM). DomainMembersPage is now at line 57, still unguarded.
    5 ⚠️ Partly done Try it out button opens More features page of invited workspace as member already passes the real route as parentRoute (TestNavigationContainer.tsx:85). It also added a domain split harness. Drop the harness change and keep only the regression test. The new tests/unit/Navigation/SplitRouterTest.ts is a good place for it.
    Other notes
    • Fix workspace navigation after distance settings deep link merged before the proposal's base SHA, so it didn't remove this trigger. It doesn't touch SplitRouter, getInitialSplitNavigatorState, or the Overview page.
    • I searched merged and open PRs since 2026-09-15 for SplitRouter, WorkspaceSplitNavigator, splitNavigatorParams, getInitialSplitNavigatorState, and 98300. None of them handles split-route params.
    • Possible follow-up: hasDifferentSplitScope.ts:23-34 reads scope only from nested routes. It would ignore the new top-level policyID, which is harmless.
    • I tried the 11-step repro on web again, but the test session loaded a blank page during onboarding. Live confirmation is still open.

    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/98300#issuecomment-5686026959) with the updates in my latest comment to create a draft PR.
    To refine: @MelvinBot <your feedback>


    view run · no recording available

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

Metadata

Metadata

Assignees

Labels

BugSomething is broken. Auto assigns a BugZero manager.ExternalAdded to denote the issue can be worked on by a contributorNot a priorityWeeklyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions