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
[$175] Split-When splitting second expense in group, Amount field gets overlapped #102615
Version Number: 9.4.97-0 Reproducible in staging?: Yes Reproducible in production?: No If this was caught during regression testing, add the test name, ID and link from BrowserStack:https://test-management.browserstack.com/projects/2219752/folder/57602041/test-cases/41237802 Email or phone of affected tester (no customers): N/A Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: iPhone 15 iOS 26.6.1 App Component: Money Requests
Action Performed:
Create a group chat with two other users
In the chat tap "+" button - Split expense - Manual
Enter an amount and split the expense
Repeat step 2, tap the amount field to enter an amount
Expected Result:
Focused Amount field is visible when creating a second split in the group chat
Actual Result:
Focused Amount field gets overlapped with the Split expense button when creating a second split in the group chat
👋 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.
⚠️Preliminary analysis (low confidence): use this as a starting point, not a conclusion.
I didn't find an App PR in 9.4.97 that plausibly causes this, so I'm not recommending a revert. None of the code behind this screen changed between production (9.4.95-4, 07da298) and staging (9.4.97-0, c318e93).
Recommendation: Ask QA to retest on production 9.4.95-4. Create two splits in a row in the same group chat and focus the amount field on the second one. Try it several times.
If it reproduces on production: demote it. This is an iOS-only overlap between the keyboard and the footer, with no data loss.
If it only reproduces on staging: an internal engineer should review the native submodule bump in this release for iOS keyboard changes. I couldn't access that repo.
Investigation details
The screen: In a group chat, "Split expense" > Manual shows the confirmation form directly. That form has an inline amount field and a "Split expense" button at the bottom (IOURequestStartPage.tsx:262). This flow is already in production.
Why it might only happen the second time (a hypothesis I haven't verified):
The code notes that closing and reopening the pane often reuses the same mounted instance (AmountField.tsx:131).
A reused instance plus that timing sensitivity would point to an older, intermittent bug rather than a regression.
Unchanged between production and staging:src/pages/iou/request/, MoneyRequestAmountForm, NumberWithSymbolForm, MoneyRequestAmountInput, ScreenWrapper/, KeyboardAvoidingView/, FixedFooter, all of SelectionList/ (including the scroll hook), AmountField.tsx and MoneyRequestConfirmationFields/.
Its @react-navigation/stack patch doesn't run on iOS, which uses the native stack (index.native.tsx:18). Its overlay and modal changes only apply on wide layouts (RightModalNavigator.tsx:289).
changed the title [-]Split-When splitting second expense in group, Amount field gets overlapped[/-][+][$175] Split-When splitting second expense in group, Amount field gets overlapped[/+]on Sep 30, 2026
Please re-state the problem that we are trying to solve in this issue.
On iOS, tapping the inline Amount field on a group chat split lifts the Split expense button above the keyboard and the button lands on top of the focused field. The form under it never scrolls, so the user types into a field they can't see.
What is the root cause of that problem?
The split confirmation is the embedded confirmation list on the start page, inside a ScreenWrapper with keyboard avoiding turned on.
On iOS that wrapper is keyboard-controller's KeyboardAvoidingView with the padding behavior, so when the keyboard opens the whole screen gets bottom padding equal to the keyboard height.
Inside it, the confirmation list renders the inline fields as the FlashList's footer content and the Split expense button as a sticky FixedFooter under the list. The padding shrinks the list viewport and the sticky footer rides up with it, but the FlashList keeps its content offset. Any inline input sitting below the new footer top is now covered.
The description field hit exactly this in #94274. PR #94398 fixed it by giving the list useScrollToFocusedInput: the field calls scrollFocusedInputIntoView from its onFocus, the hook waits for the keyboard-controller keyboardDidShow event, measures the field against the list container's top, and scrolls the list so the field sits at the top of the visible area.
The amount field never got that wiring. It renders NumberWithSymbolForm with no onFocus at all and doesn't read the scroll callback from the fields context, even though the context exposes it for every inline field.
Why a group split and not a 1:1 expense: in a 1:1 manual expense the amount input sits near the top of the form, above where the footer lands. In a group split it sits under a Paid by row plus one row per participant, which is where the video shows it. The keyboard fully opens with the field still visible, then the footer lands on it and the rows above never move. I couldn't find App state that differs between the first and second split in the chat. The field is covered on any open where the footer lifts over it while the list is still at its top, and the fix below removes that ordering dependency instead of chasing it.
What changes do you think we should make in order to solve the problem?
Give the amount field the same opt-in the description field has: a ref on its outer container and an onFocus that asks the list to scroll the field into view. The container ref is what gets measured, so the label and top border come into view too, same as the description field.
On current main the amount input already has an onFocus that sets the automatic-hint focused flag. The scroll call goes into that handler and the ref goes on the outer row view.
Same two lines on the merchant input, which sits even lower on the form.
The hook waits for keyboardDidShow plus 100 ms before measuring, so the padding and the shrunken viewport are already in place when the scroll runs. The scroll is clamped by the range that viewport allows, and since the merchant, description and date rows sit below the amount, even a clamped scroll leaves the amount above the footer.
Web keeps the no-op version of the hook, so nothing changes there.
Tests: Not run yet, this is what I'd add with the PR:
iOS and Android, group chat with two other members, Split expense > Manual, tap Amount: the field scrolls up and stays visible above the Split expense button, typing shows.
Repeat in the same chat after splitting once, same result.
Tap Merchant with the keyboard already open from Amount: the list moves again, no wait for a keyboard event.
1:1 manual expense and the standalone confirmation RHP: Amount still visible, no scroll jump when it already fits.
What alternative solutions did you explore? (Optional)
Make useScrollToFocusedInput scroll whatever input is focused on keyboardDidShow, using TextInput.State.currentlyFocusedInput(). It would cover every inline field at once, but the hook is shared with Table, where the search bar opts in explicitly, and changing that contract for every list is more than this bug needs.
Set automaticallyAdjustKeyboardInsets on the FlashList. iOS only, and the screen already pads for the keyboard, so the list would get the space twice.
Activity
applause-bot commentedon Sep 30, 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/13cbd8111cb40883a47681e57e6f917babd4d8130c4db93ea27280d8ddb74ad4
melvin-bot commentedon Sep 30, 2026
Triggered auto assignment to @marcochavezf (
DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.melvin-bot commentedon Sep 30, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Sep 30, 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:
MelvinBot commentedon Sep 30, 2026
I didn't find an App PR in 9.4.97 that plausibly causes this, so I'm not recommending a revert. None of the code behind this screen changed between production (
9.4.95-4,07da298) and staging (9.4.97-0,c318e93).Recommendation: Ask QA to retest on production 9.4.95-4. Create two splits in a row in the same group chat and focus the amount field on the second one. Try it several times.
Investigation details
The screen: In a group chat, "Split expense" > Manual shows the confirmation form directly. That form has an inline amount field and a "Split expense" button at the bottom (IOURequestStartPage.tsx:262). This flow is already in production.
Why it might only happen the second time (a hypothesis I haven't verified):
Unchanged between production and staging:
src/pages/iou/request/,MoneyRequestAmountForm,NumberWithSymbolForm,MoneyRequestAmountInput,ScreenWrapper/,KeyboardAvoidingView/,FixedFooter, all ofSelectionList/(including the scroll hook),AmountField.tsxandMoneyRequestConfirmationFields/.Candidates ruled out:
@react-navigation/stackpatch doesn't run on iOS, which uses the native stack (index.native.tsx:18). Its overlay and modal changes only apply on wide layouts (RightModalNavigator.tsx:289).MenuItemdecomposition - html valueThe other PRs in the checklist don't touch this flow. I couldn't view the bug video, so these findings come from the issue text and the code only.
Next Steps: Reply with
@MelvinBot <your feedback>to refine this analysis.view run
mitarachim commentedon Sep 30, 2026
Issue not reproduce in all PR's in this deploy, but reproduce in iOS v9.4.97-0 (staging)
melvin-bot commentedon Sep 30, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @TaduJR (
External)[-]Split-When splitting second expense in group, Amount field gets overlapped[/-][+][$175] Split-When splitting second expense in group, Amount field gets overlapped[/+]melvin-bot commentedon Sep 30, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022105363206715431407
marcochavezf commentedon Sep 30, 2026
Demoted since it might be fixed in the current PRs to deploy
melvin-bot commentedon Oct 5, 2026
@marcochavezf, @TaduJR Eep! 4 days overdue now. Issues have feelings too...
yuvrajangadsingh commentedon Oct 7, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On iOS, tapping the inline Amount field on a group chat split lifts the Split expense button above the keyboard and the button lands on top of the focused field. The form under it never scrolls, so the user types into a field they can't see.
What is the root cause of that problem?
The split confirmation is the embedded confirmation list on the start page, inside a ScreenWrapper with keyboard avoiding turned on.
App/src/pages/iou/request/IOURequestStartPage.tsx
Lines 261 to 265 in fe288f3
App/src/pages/iou/request/IOURequestStartPage.tsx
Lines 277 to 281 in fe288f3
On iOS that wrapper is keyboard-controller's KeyboardAvoidingView with the padding behavior, so when the keyboard opens the whole screen gets bottom padding equal to the keyboard height.
App/src/components/ScreenWrapper/ScreenWrapperContainer.tsx
Lines 220 to 224 in fe288f3
App/src/components/KeyboardAvoidingView/BaseKeyboardAvoidingView/index.ios.tsx
Lines 4 to 8 in fe288f3
Inside it, the confirmation list renders the inline fields as the FlashList's footer content and the Split expense button as a sticky FixedFooter under the list. The padding shrinks the list viewport and the sticky footer rides up with it, but the FlashList keeps its content offset. Any inline input sitting below the new footer top is now covered.
App/src/components/MoneyRequestConfirmationList.tsx
Lines 654 to 667 in fe288f3
App/src/components/SelectionList/SelectionListWithSections/BaseSelectionListWithSections.tsx
Lines 383 to 388 in fe288f3
App/src/components/SelectionList/components/Footer.tsx
Lines 27 to 35 in fe288f3
The description field hit exactly this in #94274. PR #94398 fixed it by giving the list
useScrollToFocusedInput: the field callsscrollFocusedInputIntoViewfrom itsonFocus, the hook waits for the keyboard-controllerkeyboardDidShowevent, measures the field against the list container's top, and scrolls the list so the field sits at the top of the visible area.App/src/components/SelectionList/hooks/useScrollToFocusedInput/index.native.ts
Lines 75 to 84 in fe288f3
App/src/components/SelectionList/hooks/useScrollToFocusedInput/index.native.ts
Lines 100 to 104 in fe288f3
App/src/components/MoneyRequestConfirmationList/sections/DescriptionField.tsx
Lines 103 to 110 in fe288f3
The amount field never got that wiring. It renders
NumberWithSymbolFormwith noonFocusat all and doesn't read the scroll callback from the fields context, even though the context exposes it for every inline field.App/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx
Lines 298 to 318 in fe288f3
App/src/components/MoneyRequestConfirmationFields/context.ts
Lines 38 to 39 in fe288f3
The merchant input has the same gap, its
onFocusonly flips a local focused flag.App/src/components/MoneyRequestConfirmationList/sections/MerchantField.tsx
Lines 116 to 125 in fe288f3
Why a group split and not a 1:1 expense: in a 1:1 manual expense the amount input sits near the top of the form, above where the footer lands. In a group split it sits under a Paid by row plus one row per participant, which is where the video shows it. The keyboard fully opens with the field still visible, then the footer lands on it and the rows above never move. I couldn't find App state that differs between the first and second split in the chat. The field is covered on any open where the footer lifts over it while the list is still at its top, and the fix below removes that ordering dependency instead of chasing it.
What changes do you think we should make in order to solve the problem?
Give the amount field the same opt-in the description field has: a ref on its outer container and an
onFocusthat asks the list to scroll the field into view. The container ref is what gets measured, so the label and top border come into view too, same as the description field.App/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx
Line 69 in fe288f3
App/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx
Lines 298 to 318 in fe288f3
NumberWithSymbolFormalready forwardsonFocusto its text input indisplayAsTextInputmode, so nothing changes there.App/src/components/NumberWithSymbolForm.tsx
Line 565 in fe288f3
onFocusthat sets the automatic-hint focused flag. The scroll call goes into that handler and the ref goes on the outer row view.keyboardDidShowplus 100 ms before measuring, so the padding and the shrunken viewport are already in place when the scroll runs. The scroll is clamped by the range that viewport allows, and since the merchant, description and date rows sit below the amount, even a clamped scroll leaves the amount above the footer.Tests: Not run yet, this is what I'd add with the PR:
What alternative solutions did you explore? (Optional)
useScrollToFocusedInputscroll whatever input is focused onkeyboardDidShow, usingTextInput.State.currentlyFocusedInput(). It would cover every inline field at once, but the hook is shared with Table, where the search bar opts in explicitly, and changing that contract for every list is more than this bug needs.automaticallyAdjustKeyboardInsetson the FlashList. iOS only, and the screen already pads for the keyboard, so the list would get the space twice.melvin-bot commentedon Oct 7, 2026
@marcochavezf, @TaduJR Still overdue 6 days?! Let's take care of this!
marcochavezf commentedon Oct 8, 2026
I think this might be already fixed, feel free to re-open or comment if it's still reproducible