Disable Reply in thread on moved system messages - #99721
Conversation
Gate MOVED / MOVEDTRANSACTION system messages out of the thread UI unless they already have a threaded child, so the doomed thread-creation call is never made and the Unexpected error creating this chat message stops appearing. Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>
| // Moved system messages can't be threaded server-side, so offering "Reply in thread" leads to a failed thread-creation call. | ||
| // Gate them out of the thread UI, but keep any moved actions that have already been legitimately threaded. |
There was a problem hiding this comment.
@MelvinBot Better to add in function jsdoc similar to others, not here
There was a problem hiding this comment.
Done — moved the note into the shouldDisableThread JSDoc as a new bullet alongside the other conditions, and removed the inline comment. Pushed as 4da019c.
|
🤖 Ran the testing steps from this PR on Web. Outcome: Setup performed: created a workspace, two manual expenses (Test Merchant A €25, Test Merchant B €40) each in its own report, submitted both reports, then used Spend → Expenses → "Move to report" to move Test Merchant A's expense into the Test Merchant B report — producing a MOVED system message.
ScreenshotsMoved expense system message rendered in chat
Report B now holds 2 expenses (€65.00) after the move — no error toast
Hover over the moved system message — no context-menu toolbar rendered
Baseline hover on a control message — also no toolbar (session-wide limitation)
|
Co-authored-by: mkhutornyi <mkhutornyi@users.noreply.github.com>




Explanation of Change
Moved system messages (
MOVED/MOVEDTRANSACTION) can't be threaded server-side, but the app still offered "Reply in thread" on them. Tapping it fired a thread-creation call against an action the server never adopted, which failed and surfaced "Unexpected error creating this chat. Please try again later."The earlier fix (Expensify/App#95979) only closed the narrow "create report from a draft expense" repro. This change gates moved system messages out of the thread UI at the source, in
shouldDisableThread, so the doomed thread-creation call is never made.I used the guarded approach from the issue rather than blanket-adding the actions to
THREAD_DISABLED: threading is disabled only when the moved action has no threaded child (!reportAction?.childVisibleActionCount), mirroring how deleted/archived actions are already handled. This preserves any moved actions users have legitimately threaded.As noted in the issue, the backend root-cause (why the moved action can't be threaded at all) is tracked internally and is out of scope here — this is the defensive FE fix that stops the user-facing error.
AI Tests run locally
npm test -- tests/unit/ReportUtilsTest.ts -t "shouldDisableThread"→ pass (17 tests, incl. 4 new)npm run typecheck→ passnpx eslinton changed files → 0 errors (only pre-existing grandfathered warnings)oxfmtformatting → cleanFixed Issues
$ #99653
PROPOSAL: #99653
Tests
Offline tests
Same as tests.
QA Steps
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.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari