[$250] Accounting reporting tabs (Statements/Unapproved card) aren't updating in real time
If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!
Version Number: 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: @miljakljajic Slack conversation (hyperlinked to channel name): #Convert
Action Performed:
- Set up a card feed on a workspace and assign a card to a user
- Add a category to several card transactions
- Ensure there are unapproved transactions showing in Unapproved card
- Update the category on the previously categorised transactions via Reports > Reports
- Attempt to edit the category again via Reports > Unapproved card
Expected Result:
Updating a transaction’s category from the Reports tab should immediately reflect in the Unapproved Card tab. Both tabs should display consistent real-time data.
Actual Result:
Trying to edit the category from the Unapproved card tab gives a "hmm...its not here" error. The category change does not update in the Unapproved Card tab until cache cleared
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
- [x] MacOS: Chrome / Safari
- [ ] MacOS: Desktop
Platforms Tested:
On which of our officially supported platforms was this issue tested:- [ ] Android: App
- [ ] Android: mWeb Chrome
- [ ] iOS: App
- [ ] iOS: mWeb Safari
- [ ] iOS: mWeb Chrome
- [x] Windows: Chrome
- [ ] MacOS: Chrome / Safari
- [ ] MacOS: Desktop
Screenshots/Videos
Add any screenshot/video evidence
The screenshots and Onyx data for the issue is in OP
Upwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~022000742154667381866
- Upwork Job ID: 2000742154667381866
- Last Price Increase: 2025-12-23
Issue Owner
Current Issue Owner: @rojiphil
Triggered auto assignment to @brianlee-expensify (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.
verifying in slack whether this should be treated as an external issue
Job added to Upwork: https://www.upwork.com/jobs/~022000742154667381866
Triggered auto assignment to Contributor-plus team member for initial proposal review - @rojiphil (External)
🚨 Edited by proposal-police: This proposal was edited at 2025-12-30 07:14:58 UTC.
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a user edits an expense from the Search interface (for eg Reports tab, **Unapproved card** tab, or Statements tab) and then navigates to a different search tab, the app displays "Hmm... it's not here" error page instead of showing the expense details.
Besides that , transaction data (category, amount...etc.) becomes stale and does not update in real-time when switching between different search tabs.
What is the root cause of that problem?
The root cause is missing snapshot synchronization for REPORT_ACTIONS and REPORT collections during expense updates in getUpdateMoneyRequestParams.
Snapshot system:
When viewing search results, the app operates in a "snapshot context" where useOnyx reads from ONYXKEYS.COLLECTION.SNAPSHOT${hash} instead of main Onyx collections. This is controlled by:
CONST.SEARCH.SNAPSHOT_ONYX_KEYSdefines which collections are redirected to snapshot:ONYXKEYS.COLLECTION.REPORTONYXKEYS.COLLECTION.REPORT_ACTIONSONYXKEYS.COLLECTION.TRANSACTIONONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS- (and others)
https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/CONST/index.ts#L7102-L7110
- The custom
useOnyxhook redirects these keys to snapshot whenisOnSearch=true:
https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/hooks/useOnyx.ts#L50-L78
- Critical:
SearchScopeProvidersetsisOnSearch=trueand only wraps theSearchListcomponent insideSearch/index.tsx.
https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/components/Search/index.tsx#L1026-L1098
Edit pages are separate navigation routes and are NOT wrapped in SearchScopeProvider, meaning they read from main Onyx collections.
Why the "not found" error occurs:
The bug manifests through many flow , one of them for eg:
- User visits Tab A (e.g: "Unapproved card") - Snapshot A is populated with search results including
REPORT_ACTIONSandREPORTdata - User visits Tab B (e.g: "Reports") - Snapshot B is populated
- User edits an expense category in Tab B:
- Main Onyx collections are updated correctly
- Snapshot B is updated with
TRANSACTION_VIOLATIONSonly - Snapshot A is NOT updated with
REPORT_ACTIONSorREPORTchanges
- User returns to Tab A:
- If cached Snapshot A is used before a new search API completes, it has stale data
- Components within
SearchScopeProvider(likeSearchList) read staleREPORT_ACTIONS/REPORT
- User attempts to view/edit the same expense from Tab A:
- The
useShowNotFoundPageInIOUStephook readsreportActionandiouReport - While edit pages normally read from main Onyx (because they're outside SearchScopeProvider), there can be timing issues during navigation where stale snapshot data affects the render
- The
The gap in getUpdateMoneyRequestParams:
Currently, getUpdateMoneyRequestParams syncs TRANSACTION_VIOLATIONS to the snapshot:
https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/libs/actions/IOU/index.ts#L4953-L4973
However, it does NOT sync:
-
REPORT_ACTIONS(the transaction thread's report actions): https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/libs/actions/IOU/index.ts#L4621-L4627 -
REPORT(the iouReport, updated at ) :
https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/libs/actions/IOU/index.ts#L4712-L4722
This inconsistency means edits made from one search tab don't propagate to other cached search snapshots.
Specific reproduction conditions:
Based on the issue report, the bug specifically requires:
- Company card transactions with a card feed assigned to a user
- Pre-cached snapshot data in the "Unapproved card" tab
- Editing via "Reports > Reports" tab, then attempting to edit via "Reports > Unapproved card"
What changes do you think we should make in order to solve the problem?
Modify getUpdateMoneyRequestParams in src/libs/actions/IOU/index.ts to add explicit snapshot synchronization for REPORT_ACTIONS and REPORT, following the existing pattern used for TRANSACTION_VIOLATIONS.
1. Sync REPORT_ACTIONS to snapshot (after line 4627):
When hash and updatedReportAction exist, sync the updated report action to the snapshot:
if (hash && updatedReportAction && transactionThread?.reportID) {
// @ts-expect-error - will be solved in https://github.com/Expensify/App/issues/73830
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
data: {
[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`]: {
[updatedReportAction.reportActionID]: updatedReportAction,
},
},
},
});
}
2. Sync iouReport to snapshot (after line 4722):
When hash exists, sync the updated iouReport to the snapshot:
if (hash && iouReport?.reportID) {
// @ts-expect-error - will be solved in https://github.com/Expensify/App/issues/73830
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
data: {
[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: {
...updatedMoneyRequestReport,
...(isTotalIndeterminate && {pendingFields: {total: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}}),
},
},
},
});
}
3. Add corresponding failureData rollbacks for both collections to maintain consistency on API failure.
Each sync block should include:
optimisticData: Immediate UI update with pending statefailureData: Rollback with original data on API failure- Defensive null guards: Check for
hash,reportID, and relevant data existence before syncing
This follows the established pattern used for TRANSACTION_VIOLATIONS sync in :
https://github.com/Expensify/App/blob/1c1ade94876825a4ecef626275bb46b708c25f5c/src/libs/actions/IOU/index.ts#L4953-L4973
What alternative solutions did you explore? (Optional)
-
Modifying useOnyx hook to always fall back to main collection: Rejected because it would defeat the purpose of snapshot isolation during search operations and could cause data inconsistency.
-
Forcing search refresh after edit: Rejected because it degrades UX with unnecessary loading states and API calls.
-
Syncing TRANSACTION to snapshot: While TRANSACTION is in
SNAPSHOT_ONYX_KEYS, the main issue is the missing REPORT_ACTIONS and REPORT sync. However, adding TRANSACTION sync would provide more complete real-time updates for category/amount changes across tabs.
@rojiphil Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸
@rojiphil 6 days overdue. This is scarier than being forced to listen to Vogon poetry!
@rojiphil @brianlee-expensify 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!
@rojiphil 8 days overdue is a lot. Should this be a Weekly issue? If so, feel free to change it!
📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸
@m-natarajan Is this still reproducible? I cannot
https://github.com/user-attachments/assets/807dac6a-4756-4e44-b890-4c8e03c27cca