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-10-01] Web - Report - Chat is not autoscrolled to the bottom after sending messages #99559
Version Number: v9.4.61-1 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from TestRail:https://github.com/Expensify/Expensify/issues/440269 Email or phone of affected tester (no customers): N/A Issue reported by: Applause Internal Team
Action Performed:
Prerequisite: Account has at least one workspace.
Open the staging.new.expensify.com website.
Open any workspace chat.
Submit expense to workspace
Go to expense detail has create from step 1
Send multiple messages to trigger a scroll
Scroll up and edit category or description
Send another message after edit catetory
Try to send another message
Note that report is not autoscrolled to the bottom.
Expected Result:
Report should be autoscrolled to the bottom after sending messages.
Actual Result:
Report is not autoscrolled to the bottom after making editions and sending messages.
Workaround:
N/A
Platforms:
Android: App
Android: mWeb Chrome
iOS: App
iOS: mWeb Safari
iOS: mWeb Chrome
Windows: Chrome
MacOS: Chrome / Safari
Screenshots/Videos
Bug7236768_1787096211948.Scr.mp4Upwork Automation - Do Not Edit
ℹ️ Confidence: Medium — root cause is pinned to a concrete, statically-provable code mechanism, but I couldn't get a live web repro to confirm the final micro-step (the test session hit an environment/infra issue). The behavior is already confirmed reproducible in staging & production by Applause.
What is the root cause of that problem?
The expense-report view (MoneyRequestReportActionsList) has its own autoscroll logic — it does not use the shared inverted-chat live-tail hook. Its "should I autoscroll on a new message?" decision hinges on a single flag, hasNewestReportAction, computed as:
When you inline-edit a transaction's category/description, an optimistic MODIFIED_EXPENSE action is written into the transaction thread. That write advances the newest-action timestamp on one side of that equality but not the other (the optimistic edit updates lastReadTime but does not bump lastVisibleActionCreated, UpdateMoneyRequest.ts:1838-1870), so the equality breaks and hasNewestReportAction flips to false.
With that flag stuck false, the position-based autoscroll hook refuses to fire on your next message because of its hasNewestReportAction && prevHasNewestReportAction guard (useScrollToEndOnNewMessageReceived.ts:65-82). Because this list is not inverted, nothing else pins it to the bottom, so the report stays where you left it — exactly the reported symptom. Sending a message without first editing works because hasNewestReportAction is still true.
Investigation details & evidence
Two autoscroll paths exist in this view:
Path A (unconditional, own sent comment):subscribeToNewActionEvent → scrollToBottomForCurrentUserAction → scrollToBottom() → scrollToIndex(lastItemIndexRef) (MoneyRequestReportActionsList.tsx:569-625). Statically this looks robust.
getReportLastVisibleActionCreated returns max(report, transactionThread) last-visible timestamps (ReportUtils.ts:13211-13215), so a stale/desynced timestamp on either side breaks the hasNewestReportAction equality.
The flip is cleanest for one-transaction expense reports (where transactionThreadReport is populated), which matches the repro steps (submit one expense → open its detail).
Secondary contributor: the scroll-offset used by Path B (scrollOffsetRef / scrollingVerticalBottomOffset) is only updated inside the scroll handler onTrackScrolling. An inline edit changes content height (popover/inline cell open/close + the appended row) without necessarily emitting a scroll event, so the offset can also go stale and independently suppress Path B.
Ruled out: inline category/description editing is a popover / in-cell interaction, not an RHP push/pop, so "returning from a modal resets scroll" does not apply — the list stays mounted.
Open question needing a live repro to confirm: whether the failure is purely Path B being gated off by prevHasNewestReportAction=false, or whether Path A's scrollToIndex also fails to land because FlashList maintainVisibleContentPosition re-anchors the viewport after the edit changed content above (MoneyRequestReportUnifiedList.tsx:346).
What changes do you think we should make in order to solve the problem?
Make the autoscroll decision robust to an inline edit desyncing hasNewestReportAction:
Preferred: when the current user sends a message, always scroll to the bottom regardless of hasNewestReportAction (i.e. rely on the unconditional Path A / scrollToBottomForCurrentUserAction and confirm it actually lands after an edit). Sending your own message is an unambiguous intent to jump to the bottom.
And/or stop letting an inline MODIFIED_EXPENSE edit flip hasNewestReportAction — either exclude MODIFIED_EXPENSE actions from the lastAction/lastVisibleActionCreated comparison at MoneyRequestReportActionsList.tsx:228-229, or ensure the optimistic edit keeps both timestamps consistent so the flag stays accurate.
What alternative solutions did you explore? (Optional)
Refresh scrollOffsetRef after inline-edit-driven layout changes (so the 250px near-bottom gate in Path B isn't evaluated against a stale offset). This addresses the secondary contributor but not the primary hasNewestReportAction flip, so it's a partial fix at best.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/99559) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Preferred: when the current user sends a message, always scroll to the bottom regardless of hasNewestReportAction (i.e. rely on the unconditional Path A / scrollToBottomForCurrentUserAction and confirm it actually lands after an edit). Sending your own message is an unambiguous intent to jump to the bottom.
And/or stop letting an inline MODIFIED_EXPENSE edit flip hasNewestReportAction — either exclude MODIFIED_EXPENSE actions from the lastAction/lastVisibleActionCreated comparison at MoneyRequestReportActionsList.tsx:228-229, or ensure the optimistic edit keeps both timestamps consistent so the flag stays accurate.
so Melvin's preferred fix is basically what the code already does. i think the failure is in this scroll path after expense edit, not in timestamp guard. what do you think?
[Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.
Link to the comment on the PR: No specific PR. We had an outdated external dependency.
Link to the comment on the Issue:
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: NA
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
This is already covered in the existing regression test. We don't need a new one.
Activity
MelvinBot commentedon Aug 26, 2026
Proposal
What is the root cause of that problem?
The expense-report view (
MoneyRequestReportActionsList) has its own autoscroll logic — it does not use the shared inverted-chat live-tail hook. Its "should I autoscroll on a new message?" decision hinges on a single flag,hasNewestReportAction, computed as:(
MoneyRequestReportActionsList.tsx:228-229)When you inline-edit a transaction's category/description, an optimistic
MODIFIED_EXPENSEaction is written into the transaction thread. That write advances the newest-action timestamp on one side of that equality but not the other (the optimistic edit updateslastReadTimebut does not bumplastVisibleActionCreated,UpdateMoneyRequest.ts:1838-1870), so the equality breaks andhasNewestReportActionflips tofalse.With that flag stuck false, the position-based autoscroll hook refuses to fire on your next message because of its
hasNewestReportAction && prevHasNewestReportActionguard (useScrollToEndOnNewMessageReceived.ts:65-82). Because this list is not inverted, nothing else pins it to the bottom, so the report stays where you left it — exactly the reported symptom. Sending a message without first editing works becausehasNewestReportActionis still true.Investigation details & evidence
subscribeToNewActionEvent→scrollToBottomForCurrentUserAction→scrollToBottom()→scrollToIndex(lastItemIndexRef)(MoneyRequestReportActionsList.tsx:569-625). Statically this looks robust.hasNewestReportAction):useScrollToEndOnNewMessageReceived.ts:65-82. This is the path the edit breaks.getReportLastVisibleActionCreatedreturnsmax(report, transactionThread)last-visible timestamps (ReportUtils.ts:13211-13215), so a stale/desynced timestamp on either side breaks thehasNewestReportActionequality.transactionThreadReportis populated), which matches the repro steps (submit one expense → open its detail).scrollOffsetRef/scrollingVerticalBottomOffset) is only updated inside the scroll handleronTrackScrolling. An inline edit changes content height (popover/inline cell open/close + the appended row) without necessarily emitting a scroll event, so the offset can also go stale and independently suppress Path B.Open question needing a live repro to confirm: whether the failure is purely Path B being gated off by
prevHasNewestReportAction=false, or whether Path A'sscrollToIndexalso fails to land because FlashListmaintainVisibleContentPositionre-anchors the viewport after the edit changed content above (MoneyRequestReportUnifiedList.tsx:346).What changes do you think we should make in order to solve the problem?
Make the autoscroll decision robust to an inline edit desyncing
hasNewestReportAction:hasNewestReportAction(i.e. rely on the unconditional Path A /scrollToBottomForCurrentUserActionand confirm it actually lands after an edit). Sending your own message is an unambiguous intent to jump to the bottom.MODIFIED_EXPENSEedit fliphasNewestReportAction— either excludeMODIFIED_EXPENSEactions from thelastAction/lastVisibleActionCreatedcomparison atMoneyRequestReportActionsList.tsx:228-229, or ensure the optimistic edit keeps both timestamps consistent so the flag stays accurate.What alternative solutions did you explore? (Optional)
Refresh
scrollOffsetRefafter inline-edit-driven layout changes (so the 250px near-bottom gate in Path B isn't evaluated against a stale offset). This addresses the secondary contributor but not the primaryhasNewestReportActionflip, so it's a partial fix at best.Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/99559)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
melvin-bot commentedon Aug 26, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @Pujan92 (
External)mukhrr commentedon Aug 26, 2026
I don't think this can fix the issue @Pujan92
Javokhiir commentedon Aug 26, 2026
@Pujan92 i looked a bit deeper. i don't think hasNewestReportAction can be the main RCA here. when current user sends a message, MoneyRequestReportActionsList already uses scrollToBottomForCurrentUserAction -> scrollToBottom(), which doesn't check that flag.
so Melvin's preferred fix is basically what the code already does. i think the failure is in this scroll path after expense edit, not in timestamp guard. what do you think?
Uzaifm127 commentedon Aug 31, 2026
melvin-bot commentedon Aug 31, 2026
@Pujan92 Huh... This is 4 days overdue. Who can take care of this?
Pujan92 commentedon Sep 1, 2026
I will review max by tomorrow
66 remaining items
melvin-bot commentedon Sep 30, 2026
Payment Summary
Resolving PRs:
Upwork Job
BugZero Checklist (@mallenexpensify)
Uzaifm127 commentedon Oct 1, 2026
@mallenexpensify The payment due here is $250 as this issue was worked before we announced $175 and this issue was
Help wanted. TY!@mananjadhav Could you please provide the checklist here? Thanks
Uzaifm127 commentedon Oct 2, 2026
@mallenexpensify Friendly bump here for payment processing. TY!
mallenexpensify commentedon Oct 3, 2026
Payment Summary
Contributor: @Uzaifm127 paid $250 via Upwork
Contributor+: @mananjadhav due $250 via NewDot
@Uzaifm127 can you please accept the job below? Please reply here and tag me once you have.
https://www.upwork.com/jobs/~022106398583399929043
@mananjadhav plz complete the Contributor+ checklist and tag me in a post once you have. Thx
Uzaifm127 commentedon Oct 3, 2026
@mallenexpensify Accepted the offer, Thank you.
mallenexpensify commentedon Oct 3, 2026
@Uzaifm127 paid, summary updated above. Thx.
@mananjadhav plz complete the Contributor+ checklist and tag me in a post once you have. Thx
mananjadhav commentedon Oct 5, 2026
Contributor+ Checklist:
[Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.
Link to the comment on the PR: No specific PR. We had an outdated external dependency.
Link to the comment on the Issue:
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: NA
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
This is already covered in the existing regression test. We don't need a new one.
mananjadhav commentedon Oct 5, 2026
@mallenexpensify The checklist is updated.
garrettmknight commentedon Oct 19, 2026
$250 approved for @mananjadhav