Skip to content

When creating an expense from FAB, going back from the Confirm details page to the amount step and next flow again takes the user to page not found instead of the confirmation page. #99429

Closed

Description

@m-natarajan

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.57-3
Reproducible in staging?: Needs reproduction (Unable to reproduce)
Reproducible in production?: Needs Reproduction
If this was caught during regression testing, add the test name, ID and link from BrowserStack:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Expensify/Expensify Issue URL:
Issue reported by: @suneox
Slack conversation (hyperlinked to channel name): #Expensify Bugs

Action Performed:

  1. Log in with an account whose default workspace is a group (paid) workspace.
  2. Click the + (global create / FAB) button → Create expense.
  3. On the Manual tab, enter an amount (e.g. 100,000) and click Next → the Confirm details page opens.
  4. (optional, as originally reported) Tap Add receipt, upload a receipt file → the app returns to Confirm details.
  5. Click the Back arrow in the Confirm details header → the app returns to the amount step.
  6. Enter/keep the amount and click Next again.

Expected Result:

Pressing Next again returns the user to the confirm page

Actual Result:

The app navigates to /not-found

Workaround:

Unknown

Platforms:

Select the officially supported platforms where the issue was reproduced:

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

Screenshots/Videos

CleanShot.2026-08-25.at.11.50.56.1.mp4

View all open jobs on GitHub

Issue OwnerCurrent Issue Owner: @FitseTLT

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Aug 25, 2026
  2. exfy-zapier commented on Aug 25, 2026

    @exfy-zapier

    This has been labelled "Needs Reproduction". Follow the steps here: https://stackoverflowteams.com/c/expensify/questions/16989

  3. MelvinBot commented on Aug 25, 2026

    @MelvinBot
    Contributor

    Proposal

    Confidence: Medium — root cause is pinned in code, but I couldn't confirm at runtime: the test account had no default group workspace, so my web repro took the self‑DM path (extra "Choose recipient" step) and correctly did not hit the bug. That's consistent with this being group‑workspace‑only.

    What is the root cause of that problem?

    The first Next and the second Next take different navigation branches inside submitGlobalCreate.

    • First Next → isReturningFromConfirmationPage is false (no participants yet) → navigateToDefaultWorkspace, which explicitly sets the draft's reportID + participants and navigates with a known‑good targetReport.reportID. → Confirm details opens ✅
    • Second Next (after Back) → participants now exist → takes the returning branch, which rebuilds the route from the existing draft: chatReportID = selectedReport?.chatReportID ?? selectedReport?.reportID, where selectedReport = getReportOrReportDraftForAmount(transaction.reportID).

    When that lookup can't resolve a report after Back, chatReportID is undefined. navigateToExistingParticipantConfirmation passes it straight through, and ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute has no guard for an undefined reportID — it builds .../confirmation/<transactionID>/undefined. The confirmation screen's report guard (withWritableReportOrNotFound / AccessOrNotFoundWrapper) then renders /not-found.

    It only reproduces with a group (paid) default workspace because this whole path is gated on shouldUseDefaultExpensePolicy requiring a group policy; a personal default instead routes to the participant page, so the returning branch is never hit.

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

    In the returning‑from‑confirmation branch of submitGlobalCreate, when selectedReport / chatReportID can't be resolved, fall back to navigateToDefaultWorkspace (which re‑assigns participants + a valid reportID) instead of navigating with a stale/undefined reportID. As defense‑in‑depth, harden ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute so it doesn't emit a route with an undefined/'undefined' reportID.

    What alternative solutions did you explore? (Optional)

    Confirming the exact failing value at runtime (whether transaction.reportID is undefined vs. a reportID missing from the report snapshot after Back) — worth a quick trace of transaction.reportID/chatReportID at IOUAmountSubmission.ts:530-536 with a group‑workspace account.

    Related issues

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


    view run

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

    @melvin-bot

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

  6. melvin-bot commented on Aug 25, 2026

    @melvin-bot

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

  7. suneox commented on Aug 25, 2026

    @suneox
    Contributor

    @FitseTLT if you can't reproduce it, I can take over this issue to verify the solution

  8. mukhrr commented on Aug 26, 2026

    @mukhrr
    Contributor

    @FitseTLT if you can't reproduce it, I can take over this issue to verify the solution

    i can reproduce this as well

  9. nkdengineer commented on Aug 26, 2026

    @nkdengineer
    Contributor

    I can reproduce it easily. chatReportID of the WS chat is an empty string after resetting the cache (result from OpenApp API). After going to the confirmation page, transaction?.reportID is the policy expense chat; when going back to the amount page and clicking the Next button, the chatReportID here is an empty string, which makes the route invalid and /not-found appears. We should use || operator instead of ?? to fix the case chatReportID is an empty string, or only use chatReport if the selectedReport is money request report (expense or iou)

    const chatReportID = selectedReport?.chatReportID ?? selectedReport?.reportID;

    Screen.Recording.2026-08-26.at.16.06.31.mov
    Image
  10. FitseTLT commented on Aug 28, 2026

    @FitseTLT
    Contributor

    🆘 C+ Help Wanted

  11. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Aug 28, 2026
  12. 35 remaining items

  13. abdullahitijani commented on Sep 6, 2026

    @abdullahitijani

    Proposal

    What is the root cause of that problem?

    The second Next after Back reaches the returning-from-confirmation branch of submitGlobalCreate (IOUAmountSubmission.ts:529-536), which builds the confirmation route's :reportID segment from selectedReport?.chatReportID ?? selectedReport?.reportID (IOUAmountSubmission.ts:534).

    When the default workspace is a paid group workspace and its expense chat currently holds an open expense report, the server sends that chat's chatReportID down as the empty string "". ?? only falls back for null and undefined, not for "", so line 534 yields "" and the route becomes create/submit/confirmation/<transactionID>/ with an empty segment. No screen route matches that, and the flow lands on /not-found. Measured on a real account: the key is absent before an expense exists, becomes "" after an open expense report is created and the account reloads, and disappears again once the expense is deleted. The optimistic write after Create expense does not carry the field, which is why a fresh-session repro does not trigger it.

    With newManualExpenseFlow on, the amount step no longer mounts for create, so this branch is unreachable. Accounts outside that beta still run the old amount-first flow and still hit the empty segment, so the guard is still needed.

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

    Make line 534 treat an empty string as missing, since "" is never a valid report ID:

    const chatReportID = selectedReport?.chatReportID || selectedReport?.reportID;

    • For the reported case the empty chatReportID falls back to the expense chat's own reportID, producing a valid create/submit/confirmation/<transactionID>/<reportID> and the confirmation page renders as expected.
    • For every existing case where chatReportID holds a real value the behavior is identical to today, and the first-Next path (navigateToDefaultWorkspace) is untouched.

    Add a case to the existing submitGlobalCreate suite in tests/unit/IOUAmountSubmissionTest.ts: a selected report with chatReportID: '' and a real reportID, asserting the confirmation route is built from the reportID and not an empty segment.

    What alternative solutions did you explore? (Optional)

    • Resolve the bound report through isMoneyRequestReport and getReportOrReportDraftForAmount before reading an ID, with a navigateToDefaultWorkspace fallback (IOUAmountSubmission.ts:525) when nothing resolves. Also correct and it covers a changed-recipient edge case, but it is a larger change than the single empty-string bug that produces this /not-found.
    • Appending ?? iouReportID to the chain. Does not fix the report: ?? leaves "" untouched, and the report's failure is exactly the empty string.

    Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

  14. melvin-bot commented on Sep 7, 2026

    @melvin-bot

    @FitseTLT Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

  15. yusufdeveloper2903 commented on Sep 8, 2026

    @yusufdeveloper2903
    Contributor

    @FitseTLT Proposal updated — not reproducible with the beta on (INVOICE keeps the amount step but never enters this branch, and the embedded confirmation's amount edit returns on backTo before submitGlobalCreate), still live without it on 7df2d24b79.

    I also measured the route Navigation.navigate receives for the four candidate derivations across five report shapes: ||, getNonEmptyStringOnyxID(...) and the combined guard print identical routes and all send an expense report with an empty chatReportID to that report's own ID, which is not a chat — the type check alone changes exactly the reported row.

  16. melvin-bot commented on Sep 8, 2026

    @melvin-bot

    @FitseTLT this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!

  17. melvin-bot commented on Sep 9, 2026

    @melvin-bot

    @FitseTLT Huh... This is 4 days overdue. Who can take care of this?

  18. FitseTLT commented on Sep 10, 2026

    @FitseTLT
    Contributor

    Not able to reproduce

  19. trasnake87 commented on Sep 11, 2026

    @trasnake87
    Contributor

    Proposal updated. @FitseTLT which branch are you testing on? That decides whether the steps exist at all.

    On main and staging the second Next is gone: #100215 (merged 2026-09-09) removed the newManualExpenseFlow flag, so shouldEmbedConfirmation is now shouldUseTab || iouType === PAY and the Manual tab renders the confirmation inline for create. submitGlobalCreate is untouched, but the FAB → Create expense → Manual path no longer has a second Next to press.

    Production still has the gate, and there it needs three things at once:

    1. An account not on the newManualExpenseFlow beta.
    2. The default expense policy set to a group workspace ("Set as default workspace").
    3. An open expense report on that workspace's expense chat: create one expense on it and leave the report unsubmitted, then reload. Before the reload the chat's chatReportID key is absent; after it the server sends chatReportID: "", and that empty string is what reaches the route.

    Then FAB → Create expense → Manual → amount → Next → Back → Next lands on /not-found. Same account, same steps with only that field cleared in Onyx: Confirm details. Recorded on a web dev build with production's gating (submitGlobalCreate byte-identical to main today), URL bar visible, one take: bug at 0:42, control at 1:28. Setting up step 3 on camera: setup clip. The "beta off" state is simulated by removing the beta from Onyx on the test account, which is otherwise on the beta; that is stated on screen.

    Correction to my 09-06 note: deleting the expense only clears the field when no other open expense remains on that chat.

  20. melvin-bot commented on Sep 15, 2026

    @melvin-bot

    @FitseTLT Huh... This is 4 days overdue. Who can take care of this?

  21. trjExpensify commented on Sep 17, 2026

    @trjExpensify
    Contributor

    Closing as the newManualExpenseFlow beta has been deprecated, so it's the "new normal".

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.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorHelp WantedApply this label when an issue is open to proposals by contributorsNeeds ReproductionReproducible steps needed

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions