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-09-23] Spend- Unable to submit report modal does not open when one of the reports cannot be submitted #99028
Version Number: 9.4.57-0 Reproducible in staging?: Yes Reproducible in production?: N/A - new feature, doesn't exist in prod If this was caught during regression testing, add the test name, ID and link from BrowserStack:#96922 Email or phone of affected tester (no customers):sdsiodjnosdjiodsj@gmail.com Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: Mac 26.5 / Chrome App Component: Money Requests
👋 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.
Classification: Frontend bug Causing PR: #96922 - "Split held expenses onto a new Draft report when submitting offline" by @c3024 (High confidence) Related Issues: None
Recommendation: ROLL FORWARD
The bulk (multi-select) Submit guard checks hasOnlyHeldExpenses against a single flattened list of transactions spanning all selected reports, so it only fires when every selected transaction is held. The fix is a small, well-scoped change (evaluate the guard per report), which is far less risky than reverting this 28-file feature PR.
Assigned: @c3024 (PR author), @NikkiWines and @huult (approving reviewers) from PR #96922 — already assigned Labels: No change. DeployBlockerCash is kept (App deploy is blocked). DeployBlocker (Web) is not present, so nothing to remove.
The feature is new and does not exist in production, matching "Reproducible in production?: N/A - new feature".
The single-report Submit case works (correctly opens the modal); only the multi-report case fails — exactly the signature of a per-report predicate being applied to a cross-report aggregate.
The same PR applies hasOnlyHeldExpensesper report in every single-report path (submitReport in SubmitPrimaryAction.tsx, handleActionButtonPress in SubmitActionButton.tsx), confirming the intended per-report semantics that the bulk path violates.
hasOnlyHeldExpenses is a per-report predicate ("this report contains only held expenses, so it cannot be submitted"). When two reports are selected — one all-held, one submittable — the flattened list contains a non-held transaction, so hasOnlyHeldExpenses returns false, the "Unable to submit report" modal never appears, and the flow proceeds to submit (silently skipping the un-submittable held report).
The fix should group the selected transactions by report and show the block modal when any selected report consists only of held expenses (i.e. selectedReports.some(report => hasOnlyHeldExpenses(transactionsForReport))), mirroring the per-report checks already used in the single-report Submit paths. Note the hasOnlyPendingCardTransactions(allSelectedTransactionsList) guard immediately above (L2262) has the identical cross-report flattening flaw and should be fixed the same way.
💡 Suggested AI code review rule
This bug looks like a recurring, mechanically detectable pattern, so it may be worth a coding-standards rule so the AI reviewer catches the next one. Proposed for Expensify/App:
Rule:CONSISTENCY-NN Evaluate per-report guard predicates per report in bulk/multi-select flows
Condition: In a bulk/multi-select handler, a report-scoped "all/only" predicate (hasOnly*, e.g. hasOnlyHeldExpenses, hasOnlyPendingCardTransactions) is called on a transaction list aggregated across multiple reports. Group by reportID and evaluate the predicate per report (.some(report => hasOnly...(txnsForReport))) instead.
Why: A per-report "only-X" predicate applied to a cross-report flattened list returns false as soon as any other report contributes a non-X transaction, silently defeating the guard for the reports that should be blocked (this issue: the "Unable to submit" modal never opens in multi-select).
Search pattern:hasOnly[A-Za-z]+\((?:all|.*[Ss]elected).*[Tt]ransaction in bulk-action hooks/handlers (e.g. src/hooks/useSearchBulkActions.ts).
Fix (✅): group transactions by report and use selectedReports.some(report => hasOnlyHeldExpenses(transactionsForReport)), mirroring the single-report paths in this PR.
The full rule file, with the ❌/✅ code inline in Expensify/App's coding-standards format, would go in that repo. If it's worth adding, a maintainer can open it there as a draft PR.
Activity
applause-bot commentedon Aug 20, 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/80962538eb4492ad9ee6c979cdafccff6baccea406d1b8b77342a7dd1de7993e
melvin-bot commentedon Aug 20, 2026
You have been assigned to this deploy blocker because you recently merged this PR: #96922
melvin-bot commentedon Aug 20, 2026
@NikkiWines @huult you have been assigned to this deploy blocker because you reviewed the offending PR: #96922
github-actions commentedon Aug 20, 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:
melvin-bot commentedon Aug 20, 2026
@c3024 you have been assigned to this deploy blocker because you authored the offending PR: #96922
melvin-bot commentedon Aug 20, 2026
💬 A slack conversation has been started in #expensify-open-source
MelvinBot commentedon Aug 20, 2026
🔍 Investigation Summary
Classification: Frontend bug
Causing PR: #96922 - "Split held expenses onto a new Draft report when submitting offline" by @c3024 (High confidence)
Related Issues: None
Recommendation: ROLL FORWARD
The bulk (multi-select) Submit guard checks
hasOnlyHeldExpensesagainst a single flattened list of transactions spanning all selected reports, so it only fires when every selected transaction is held. The fix is a small, well-scoped change (evaluate the guard per report), which is far less risky than reverting this 28-file feature PR.Assigned: @c3024 (PR author), @NikkiWines and @huult (approving reviewers) from PR #96922 — already assigned
Labels: No change.
DeployBlockerCashis kept (App deploy is blocked).DeployBlocker(Web) is not present, so nothing to remove.📋 Detailed Analysis
Evidence
src/hooks/useSearchBulkActions.ts. The issue was reported on 2026-08-20 against v9.4.57-0, which is the deploy that includes this PR.hasOnlyHeldExpensesper report in every single-report path (submitReportinSubmitPrimaryAction.tsx,handleActionButtonPressinSubmitActionButton.tsx), confirming the intended per-report semantics that the bulk path violates.Verification
src/hooks/useSearchBulkActions.tslines 2256-2270 — the bulk SubmitonSelectedhandler.gh pr diff 96922confirms this exact block (@@ -2251,6 +2253,11 @@) was added by PR Split held expenses onto a new Draft report when submitting offline #96922.hasOnlyHeldExpensesinsrc/libs/ReportUtils.tsL11490-L11492 returnstrueonly when no transaction in the passed list is un-held.Root Cause
In the bulk Submit handler,
allSelectedTransactionsListis built by flattening the transactions of all selected reports into one array:hasOnlyHeldExpensesis a per-report predicate ("this report contains only held expenses, so it cannot be submitted"). When two reports are selected — one all-held, one submittable — the flattened list contains a non-held transaction, sohasOnlyHeldExpensesreturnsfalse, the "Unable to submit report" modal never appears, and the flow proceeds to submit (silently skipping the un-submittable held report).The fix should group the selected transactions by report and show the block modal when any selected report consists only of held expenses (i.e.
selectedReports.some(report => hasOnlyHeldExpenses(transactionsForReport))), mirroring the per-report checks already used in the single-report Submit paths. Note thehasOnlyPendingCardTransactions(allSelectedTransactionsList)guard immediately above (L2262) has the identical cross-report flattening flaw and should be fixed the same way.💡 Suggested AI code review rule
This bug looks like a recurring, mechanically detectable pattern, so it may be worth a coding-standards rule so the AI reviewer catches the next one. Proposed for
Expensify/App:CONSISTENCY-NNEvaluate per-report guard predicates per report in bulk/multi-select flowshasOnly*, e.g.hasOnlyHeldExpenses,hasOnlyPendingCardTransactions) is called on a transaction list aggregated across multiple reports. Group byreportIDand evaluate the predicate per report (.some(report => hasOnly...(txnsForReport))) instead.falseas soon as any other report contributes a non-X transaction, silently defeating the guard for the reports that should be blocked (this issue: the "Unable to submit" modal never opens in multi-select).hasOnly[A-Za-z]+\((?:all|.*[Ss]elected).*[Tt]ransactionin bulk-action hooks/handlers (e.g.src/hooks/useSearchBulkActions.ts).App/src/hooks/useSearchBulkActions.ts
Lines 2262 to 2270 in c156d5f
selectedReports.some(report => hasOnlyHeldExpenses(transactionsForReport)), mirroring the single-report paths in this PR.The full rule file, with the ❌/✅ code inline in
Expensify/App's coding-standards format, would go in that repo. If it's worth adding, a maintainer can open it there as a draft PR.view run
38 remaining items
huult commentedon Sep 17, 2026
Issue #101278 is not a regression, per this comment.
melvin-bot commentedon Sep 22, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Sep 22, 2026
Payment Summary
Resolving PRs:
Warn about blocked reports when bulk submitting a mixed selection #99995
Reviewer: @huult owed $250 via NewDot
Contributor: @c3024 does not require payment (Contractor)
BugZero Checklist (@mallenexpensify)
mallenexpensify commentedon Sep 24, 2026
Payment Summary
Contributor+: @huult due $250 via NewDot
Bit of a weird one but that above looks correct. thx
JmillsExpensify commentedon Oct 14, 2026
$250 approved for @huult