Repository navigation
Conversation
An offline OpenReport that optimistically creates a transaction thread kept the report on failure and merged errorFields.createChat onto it. SidebarUtils force-displays any report carrying errors, so the orphan surfaced in the LHN as an empty row with a Fix badge the user could not dismiss. Add shouldRemoveOptimisticReportOnFailure to openReport so app-created reports undo the create instead of decorating it, and set it for createTransactionThreadReport. User-initiated creates keep their retryable error. Also require the parent action to exist before treating it as deleted in the reportAttributes propagation guard, since isDeletedAction(undefined) is true. Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
|
|
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
🤖 Web test steps verification: ❌ not run — no browser and no documented steps I could not run this on Web. Two separate blockers:
The repro that would need to be run is the one from IOU-Chat with Fix badge and an error appears in LHN after paid offline expense was deleted: it needs two accounts on two devices, User A offline paying an expense, User B deleting it, then User A reconnecting. That is a multi-device offline flow — worth noting it is not something a single-browser automated pass verifies cleanly even when the browser works. What I did verify: the unit tests, executed locally on the PR head (
The two new cases cover both halves of the change:
Static read of the diff matches: the rollback is gated on What still needs a human on Web: the two-device offline repro from the linked issue, confirming (a) no orphaned row with a |
Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
|
Fixed and pushed — Cause: the new Fix: import type {PersonalDetails, PersonalDetailsList, Policy, Report, ...} from '@src/types/onyx';
+import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {isTrackIntentUserSelector} from '@selectors/Onboarding';
-
-import {isEmptyObject} from '@src/types/utils/EmptyObject';The only other red check is Verification
I could not verify anything in a browser this run — no dev SSL cert was available — but this change is a formatter-only import reorder with no runtime effect. |
|
@MelvinBot pull main |
…ctionThreadFixBadge Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
…om action params Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
|
Fixed the failing The two new tests passed
The |
|
@MelvinBot close this PR |
|
🤖 I couldn't close this PR. The command to close it needs approval in my environment, and that approval wasn't granted. You're an assignee, so you can close it yourself with the Close pull request button below. |
|
@MelvinBot close PR like you did in other PRs |
|
🤖 I still can't close this PR. The close command is blocked in the environment this request runs in, and I can't approve it from here. Other PRs may have been closed from a different setup that allows it. The PR is still open. As an assignee, you can close it with the Close pull request button below. |
|
@MelvinBot close PR like you did in #101153 (comment) |
|
🤖 I couldn't close this PR. This run doesn't have permission to run You're the assignee, so you can close it yourself. Or retry Once it's closed, the branch |
Explanation of Change
When a user opens an expense while offline and the app has no transaction thread for it yet,
ReportFetchHandlercallscreateTransactionThreadReport()right away instead of waiting for the report to load. That queues anOpenReportfor a thread that only exists optimistically. If the expense is deleted before the queue drains, thatOpenReportfails.On failure
openReportkept the report it had just created and mergederrorFields.createChatonto it.SidebarUtilsforce-displays any report that carries errors, before the checks that would normally hide a transaction thread. So the dead thread showed up in the LHN as its own row: fallback avatar, "No activity yet", and a redFixbadge with nothing behind it to fix. The thread is an internal artifact — the user never asked to create a chat, and there is no retry affordance on the row — so the error was not actionable and could not be dismissed.This makes
failureDatathe inverse ofoptimisticDatainstead of decorating what it created:shouldRemoveOptimisticReportOnFailureparam onopenReport. When set and the call created the report,failureDatasetsreport_<id>,reportActions_<id>andreportMetadata_<id>tonullrather than merging acreateChat/createChatThreaderror onto them.failureDatais applied after the API response's ownonyxData(OnyxUpdates.ts:74-99), so this also clears theerrorFields.createChatthe server writes on its 666 response.childReportIDis cleared too, so it no longer points at a report that does not exist.createTransactionThreadReportopts in. User-initiated creates (DM, group chat, workspace room) do not, and keep showing their retryable error exactly as before — covered by a new regression test.One related fix in the same area: the guard added by Fix RBR on DM after paying deleted expense offline calls
isDeletedAction(parentReportAction), andisDeletedAction(undefined)returnstrue. It was therefore suppressing parent-chat error propagation for any child whose parent action simply had not loaded yet. It now follows the codebase convention of checking the action exists first.Notes for the reviewer
OpenReportfails, they now get the not-found page instead of "Unexpected error creating this chat. Please try again later." That matches what already happens when another user deletes the expense you are looking at, but it is a visible difference.Fixed Issues
$ #100676
PROPOSAL: #100676 (comment)
Tests
// TODO: The human co-author must fill out the tests they ran before marking this PR as "ready for review".
// Please describe what tests you performed that validate the change works.
Offline tests
// TODO: The human co-author must fill out the offline behavior they verified. The reported repro is itself an offline flow (User A pays while offline, User B deletes the expense, User A reconnects).
QA Steps
// TODO: The human co-author must fill out the QA steps before marking this PR as "ready for review".
// Please describe what QA needs to do to validate the change and which areas they need to test for regressions.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.AI Tests
Run locally on
Expensify/Appper the test-selection matrix:npm run typecheck— passednpm run linton the changed files — passednpm run lint-changed— passednpm run spell-changed— passednpm test -- tests/actions/ReportTest.ts— 309 passed, including the two new casesSidebarUtilsTest,SidebarTest,SidebarFilterTest,ReportAttributesUtilsTest,ReportAttributesSelectorTest,OnyxDerivedTest,OnyxDerived/*,ReplaceOptimisticReportWithActualReportTest,ReportFetchHandlerTest,DeepLinkHandlerTest,ReportUtilsTest,MergeTransactionTest,AddExistingExpenseTest,IOUTest/DeleteMoneyRequestTest,IOUTest/TrackExpenseTest,IOUTest/DuplicateTest,MoneyRequestReportTransactionsNavigationTest,MoneyRequestReportPreview,TransactionThreadNavigationUtilsTest,MoneyReportTransactionThreadContext,ReportNotFoundGuardTest,ClearReportActionErrorsTest,DeleteReportTestcreateTransactionThreadReporttest was confirmed to fail without the fix (the report survives witherrorFields.createChat), so it genuinely covers the regressionCould not run: the full
npm testsuite (exceeded the run time limit — CI will cover it),npm run storybook -- --smoke-test --ci(no component or story changes), and browser verification of the repro (no dev SSL cert available in this environment).Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari