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
Inconsistency when moving all unreported expenses offline during first & second bulk select #102310
Version Number: 9.4.94-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:#98411 Email or phone of affected tester (no customers):jsdoijnodsdisojo@gmail.com Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: Mac 26.5 / Chrome App Component: Search
Action Performed:
Precondition:
Create more than 50 unreported expenses.
Go to staging.new.expensify.com
Go to Account > Troubleshoot > Clear cache and restart > Reset and refresh.
Go to Spend > Expenses.
Bulk select all unreported expenses > Select all.
Scroll down to load all expenses.
Go offline.
Click Move to report.
→ "You appear to be offline" modal is shown.
Deselect all unreported expenses.
Select all unreported expenses.
Click Move to report.
→ Report RHP opens.
Expected Result:
In Step 7, user should be able to move all unreported expenses to report in offline mode because all expenses are loaded.
Actual Result:
In Step 7, You appear to be offline" modal is shown when moving all unreported expenses to report in offline mode.
In Step 10, after deselecting and reselecting all unreported expenses, Report RHP opens.
👋 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.
Recommendation: Fix forward. The bug only happens offline, in a new feature. It blocks the move instead of losing data, and deselecting then reselecting works around it. A revert would remove the whole feature.
Proposal
What is the root cause of that problem?
"Select all" sets areAllMatchingItemsSelected = true. When you scroll and every page loads, that flag stays true. The reconcile effect leaves out totalSelectableItemsCount on purpose, and the flag is only recomputed when that count is passed.
The new offline guard checks only the flag. It does not check whether any matching expenses are still unloaded. So the first attempt shows the offline modal even though everything is loaded.
Deselect all calls toggleAll with the count, which clears the flag. On reselect, hasMoreResults is false, so the "Select all matching" option isn't shown. That makes it a plain select-all, so the guard doesn't fire and the RHP opens.
The RHP has the same flaw. If only the button is fixed, getAllMatchingQueryParams still returns the query while the flag is true. The shouldBlockOfflineAllMatchingMove check in the RHP would then block choosing a report.
What changes do you think we should make in order to solve the problem?
Treat "all matching" as unresolved only while some pages are still unloaded. After the last page loads, the explicit selection is the full set, so the move can use the transaction list both offline and online.
In the "Move expenses" guard in useSearchBulkActions.ts, block offline only when areAllMatchingItemsSelected and hasUnloadedMatchingExpenses are both true.
Add a hasMoreResults argument to getAllMatchingQueryParams. Have it return empty params when hasMoreResults is false. Pass the value from all three callers: SearchTransactionsChangeReport, DynamicNewReportWorkspaceSelectionPage, and DynamicIOURequestStepUpgrade.
Add a unit test for this case: all matching selected, hasMoreResults false, offline. "Move expenses" should open the RHP, and the params should not include jsonQuery.
What alternative solutions did you explore? (Optional)
Clear areAllMatchingItemsSelected in the selection provider after the last page loads with no exclusions. This is riskier because it also changes the footer's "All matching items selected" text and other bulk actions.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/102310) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
I think this is expected behavior based on this confirmation. Since we don't have any documentation mentioning this, if we decide to handle it, it should be tracked in a separate issue to define the expected behavior, since selecting all while offline can include expenses that haven't loaded yet.
Activity
applause-bot commentedon Sep 25, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/c6f64d3bf425215864d106578a6745a099a5ac2e088a47d4098d9d3334449fd7
melvin-bot commentedon Sep 25, 2026
You have been assigned to this deploy blocker because you recently merged this PR: #98411
github-actions commentedon Sep 25, 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:
melvin-bot commentedon Sep 25, 2026
@mjasikowski @suneox you have been assigned to this deploy blocker because you reviewed the offending PR: #98411
melvin-bot commentedon Sep 25, 2026
💬 A slack conversation has been started in #expensify-open-source
MelvinBot commentedon Sep 25, 2026
Deploy Blocker Analysis
Causing PR: Support 'Select all matching' when moving expenses to a report. It is in Deploy Checklist: New Expensify 2026-09-25. The bug is frontend only, and I'm highly confident in this cause.
Recommendation: Fix forward. The bug only happens offline, in a new feature. It blocks the move instead of losing data, and deselecting then reselecting works around it. A revert would remove the whole feature.
Proposal
What is the root cause of that problem?
"Select all" sets
areAllMatchingItemsSelected = true. When you scroll and every page loads, that flag staystrue. The reconcile effect leaves outtotalSelectableItemsCounton purpose, and the flag is only recomputed when that count is passed.The new offline guard checks only the flag. It does not check whether any matching expenses are still unloaded. So the first attempt shows the offline modal even though everything is loaded.
Deselect all calls
toggleAllwith the count, which clears the flag. On reselect,hasMoreResultsis false, so the "Select all matching" option isn't shown. That makes it a plain select-all, so the guard doesn't fire and the RHP opens.The RHP has the same flaw. If only the button is fixed,
getAllMatchingQueryParamsstill returns the query while the flag istrue. TheshouldBlockOfflineAllMatchingMovecheck in the RHP would then block choosing a report.Code references
hasUnloadedMatchingExpensesalready exists but isn't used in the guard: src/hooks/useSearchBulkActions.ts:1940-1941hasMoreResultsis true: src/components/Search/SearchList/SearchSelectAllMenu.tsx:40-52What changes do you think we should make in order to solve the problem?
Treat "all matching" as unresolved only while some pages are still unloaded. After the last page loads, the explicit selection is the full set, so the move can use the transaction list both offline and online.
useSearchBulkActions.ts, block offline only whenareAllMatchingItemsSelectedandhasUnloadedMatchingExpensesare both true.hasMoreResultsargument togetAllMatchingQueryParams. Have it return empty params whenhasMoreResultsis false. Pass the value from all three callers:SearchTransactionsChangeReport,DynamicNewReportWorkspaceSelectionPage, andDynamicIOURequestStepUpgrade.hasMoreResultsfalse, offline. "Move expenses" should open the RHP, and the params should not includejsonQuery.What alternative solutions did you explore? (Optional)
Clear
areAllMatchingItemsSelectedin the selection provider after the last page loads with no exclusions. This is riskier because it also changes the footer's "All matching items selected" text and other bulk actions.Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/102310)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
suneox commentedon Sep 25, 2026
I think this is expected behavior based on this confirmation. Since we don't have any documentation mentioning this, if we decide to handle it, it should be tracked in a separate issue to define the expected behavior, since selecting all while offline can include expenses that haven't loaded yet.
cc: @mjasikowski
mjasikowski commentedon Sep 28, 2026
Working as expected, closing