You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-04-14] Odometer - Removed photo is shown on confirm page when it is removed from Distance page #85368
Version Number: 9.3.38-0 Reproducible in staging?: Yes Reproducible in production?: N/A - new feature, doesn't exist in prod If this was caught during regression testing, add the test name, ID and link from BrowserStack:#83556 Email or phone of affected tester (no customers):applausetester+sjddjois2j1d@applause.expensifail.com Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: Mac 26.2 / Chrome App Component: Money Requests
Action Performed:
Precondition:
Log in with Expensifail account.
Go to staging.new.expensify.com
Go to workspace chat.
Click + > Track distance > Odometer.
Enter Start and End reading.
Insert Start and End photo.
Click Next.
On confirm page, click Distance.
Click End photo.
Remove the photo.
Click RHP back button.
Click Distance.
Expected Result:
Since odometer photo is removed from Distance page, receipt on confirm page will not show the removed photo.
Actual Result:
Odometer photo is removed from Distance page, but the receipt on confirm page still shows the removed photo.
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
Identify the pull request that introduced this issue and revert it.
PR #83556 introduced the odometer image stitching feature. The bug occurs because when a user deletes one of the odometer images, the stitched receipt on the confirmation page is not updated to reflect the deletion.
Labels: Keeping DeployBlockerCash (this is a frontend bug affecting the App deploy).
Evidence
The issue reproduces following steps from the bug report:
Add both start and end odometer photos
Navigate to confirmation page (stitched image created)
Go back to Distance page
Delete one of the photos
Return to confirmation page
BUG: The deleted photo still appears in the stitched receipt
Root Cause
In IOURequestStepDistanceOdometer.tsx, the navigateToNextPage function only updates the receipt when clicking Next/Save. When a user deletes an image from the Distance page and navigates back to confirmation using the RHP back button, the receipt is not re-stitched or cleared. The previously stitched image persists even though one of the source images was deleted.
The fix requires updating the receipt whenever odometer images change, not just on form submission.
Please re-state the problem that we are trying to solve in this issue.
When a user adds start and end odometer photos, navigates to the confirm page, then goes back to the Distance page and removes a photo, the confirm page still displays the old stitched receipt image that includes the removed photo.
What is the root cause of that problem?
There are two separate data paths that get out of sync:
Odometer images are stored in transaction.comment.odometerStartImage / odometerEndImage
The receipt (stitched combination of both images) is stored in transaction.receipt.source
When the user first clicks "Next" on the odometer page, navigateToNextPage() at IOURequestStepDistanceOdometer.tsx:381-398 stitches the two odometer images together and stores the result in transaction.receipt.source via setMoneyRequestReceipt().
When the user later removes a photo via the preview modal, deleteOdometerImageAndClose() at TransactionReceiptModalContent.tsx:244-250 calls removeMoneyRequestOdometerImage() at IOU/index.ts:1689-1699. This function only nullifies transaction.comment.odometerStartImage or odometerEndImage — it does not touch transaction.receipt.
When the user then navigates back to the confirm page using the RHP back button, navigateBack() at IOURequestStepDistanceOdometer.tsx:346-352 calls Navigation.goBack(confirmationRoute) directly — it does not call navigateToNextPage(), so the receipt is never re-stitched or updated. The confirm page reads the stale transaction.receipt.source at IOURequestStepConfirmation.tsx:273-274 and displays the old stitched image.
There is also a secondary issue: even if the user presses "Save" instead of the back button, navigateToNextPage() at line 390 has a condition if (stitchedImage ?? odometerStartImage ?? odometerEndImage) — if both images were removed, this condition is entirely falsy and setMoneyRequestReceipt is never called, leaving the stale receipt intact.
What changes do you think we should make in order to solve the problem?
In removeMoneyRequestOdometerImage() at src/libs/actions/IOU/index.ts, after nullifying the odometer image in transaction.comment, also clear transaction.receipt by merging receipt: {source: '', filename: ''}. This ensures the stale stitched receipt is removed immediately when any odometer photo is deleted. The receipt will be properly re-generated when the user presses Save/Next on the odometer page (via navigateToNextPage()).
As a safety net, also add an else clause to the condition at IOURequestStepDistanceOdometer.tsx:390 to clear the receipt when all odometer images have been removed:
if (stitchedImage ?? odometerStartImage ?? odometerEndImage) {
// existing code to set receipt
} else {
// Clear stale receipt when all odometer images have been removed
setMoneyRequestReceipt(transactionID, '', '', isTransactionDraft);
}
What alternative solutions did you explore? (Optional)
Only fix navigateToNextPage: Adding the else clause alone would only fix the "Save" path but not the "back without saving" path, which is the primary bug trigger per the issue steps.
Update removeMoneyRequestOdometerImage to re-stitch with the remaining image: Instead of clearing the receipt, update it to show just the remaining single image. This provides better UX (receipt stays visible with the remaining photo) but adds complexity to the action function. Could be a follow-up enhancement.
Force navigateBack to always call navigateToNextPage: This would re-stitch on every back navigation, but has side effects like recalculating distance and potentially triggering form errors unnecessarily.
Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot <your feedback> to refine this analysis, or explain why you are rejecting Melvin's proposal.
@nlemma This PR [Odometer] Move odometer image stitching to confirmation page moves this logic from odometer screen to confirmation page where it will work exactly as pointed out in expected result. With that in mind and knowing that odometer is hidden behind beta I think we can close this issue.
changed the title [-]Odometer - Removed photo is shown on confirm page when it is removed from Distance page[/-][+][Due for payment 2026-04-14] Odometer - Removed photo is shown on confirm page when it is removed from Distance page[/+]on Apr 7, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.52-9 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
@DylanDylann The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]
Activity
melvin-bot commentedon Mar 16, 2026
You have been assigned to this deploy blocker because you recently merged this PR: #83556
melvin-bot commentedon Mar 16, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Mar 16, 2026
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
OSBotify commentedon Mar 16, 2026
Investigation Summary
Classification: Frontend bug
Causing PR: #83556 - Odometer Image stitching by @jakubkalinski0 (High confidence)
Recommendation: REVERT
PR #83556 introduced the odometer image stitching feature. The bug occurs because when a user deletes one of the odometer images, the stitched receipt on the confirmation page is not updated to reflect the deletion.
Labels: Keeping DeployBlockerCash (this is a frontend bug affecting the App deploy).
Evidence
The issue reproduces following steps from the bug report:
Root Cause
In IOURequestStepDistanceOdometer.tsx, the navigateToNextPage function only updates the receipt when clicking Next/Save. When a user deletes an image from the Distance page and navigates back to confirmation using the RHP back button, the receipt is not re-stitched or cleared. The previously stitched image persists even though one of the source images was deleted.
The fix requires updating the receipt whenever odometer images change, not just on form submission.
MelvinBot commentedon Mar 16, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a user adds start and end odometer photos, navigates to the confirm page, then goes back to the Distance page and removes a photo, the confirm page still displays the old stitched receipt image that includes the removed photo.
What is the root cause of that problem?
There are two separate data paths that get out of sync:
transaction.comment.odometerStartImage/odometerEndImagetransaction.receipt.sourceWhen the user first clicks "Next" on the odometer page,
navigateToNextPage()atIOURequestStepDistanceOdometer.tsx:381-398stitches the two odometer images together and stores the result intransaction.receipt.sourceviasetMoneyRequestReceipt().When the user later removes a photo via the preview modal,
deleteOdometerImageAndClose()atTransactionReceiptModalContent.tsx:244-250callsremoveMoneyRequestOdometerImage()atIOU/index.ts:1689-1699. This function only nullifiestransaction.comment.odometerStartImageorodometerEndImage— it does not touchtransaction.receipt.When the user then navigates back to the confirm page using the RHP back button,
navigateBack()atIOURequestStepDistanceOdometer.tsx:346-352callsNavigation.goBack(confirmationRoute)directly — it does not callnavigateToNextPage(), so the receipt is never re-stitched or updated. The confirm page reads the staletransaction.receipt.sourceatIOURequestStepConfirmation.tsx:273-274and displays the old stitched image.There is also a secondary issue: even if the user presses "Save" instead of the back button,
navigateToNextPage()at line 390 has a conditionif (stitchedImage ?? odometerStartImage ?? odometerEndImage)— if both images were removed, this condition is entirely falsy andsetMoneyRequestReceiptis never called, leaving the stale receipt intact.What changes do you think we should make in order to solve the problem?
In
removeMoneyRequestOdometerImage()atsrc/libs/actions/IOU/index.ts, after nullifying the odometer image intransaction.comment, also cleartransaction.receiptby mergingreceipt: {source: '', filename: ''}. This ensures the stale stitched receipt is removed immediately when any odometer photo is deleted. The receipt will be properly re-generated when the user presses Save/Next on the odometer page (vianavigateToNextPage()).As a safety net, also add an
elseclause to the condition atIOURequestStepDistanceOdometer.tsx:390to clear the receipt when all odometer images have been removed:What alternative solutions did you explore? (Optional)
navigateToNextPage: Adding the else clause alone would only fix the "Save" path but not the "back without saving" path, which is the primary bug trigger per the issue steps.removeMoneyRequestOdometerImageto re-stitch with the remaining image: Instead of clearing the receipt, update it to show just the remaining single image. This provides better UX (receipt stays visible with the remaining photo) but adds complexity to the action function. Could be a follow-up enhancement.navigateBackto always callnavigateToNextPage: This would re-stitch on every back navigation, but has side effects like recalculating distance and potentially triggering form errors unnecessarily.Next Steps for Contributor+ team: Reply with
@MelvinBot implement thisto create a draft PR,@MelvinBot <your feedback>to refine this analysis, or explain why you are rejecting Melvin's proposal.jakubkalinski0 commentedon Mar 16, 2026
@nlemma This PR [Odometer] Move odometer image stitching to confirmation page moves this logic from odometer screen to confirmation page where it will work exactly as pointed out in expected result. With that in mind and knowing that odometer is hidden behind
betaI think we can close this issue.23 remaining items
[-]Odometer - Removed photo is shown on confirm page when it is removed from Distance page[/-][+][Due for payment 2026-04-14] Odometer - Removed photo is shown on confirm page when it is removed from Distance page[/+]melvin-bot commentedon Apr 7, 2026
Reviewinglabel has been removed, please complete the "BugZero Checklist".melvin-bot commentedon Apr 7, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.52-9 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
If no regressions arise, payment will be issued on 2026-04-14. 🎊
For reference, here are some details about the assignees on this issue:
melvin-bot commentedon Apr 7, 2026
@DylanDylann The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]
melvin-bot commentedon Apr 7, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)