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] Search - Error and From filter dropped after entering "group-by: reports" on a saved search (follow-up to #89168) #104287
fix: prevent search error when using group-by reports fixed the originally reported case. During regression testing, QA could still reproduce the bug on staging v9.5.6-0 by following the regression test steps below. The contributor and C+ agreed this is a separate flow that should be handled in a new issue (discussion).
Version Number: v9.5.6-0 Reproducible in staging?: Yes Reproducible in production?: Not checked App Component: Search
Action Performed:
Precondition:
Log in with an account that has some expenses.
Steps:
Go to Spend > Expenses.
Open Advanced filters.
Select From.
Select the account you are currently logged in with.
Apply the filter.
Save the search.
Click View results.
In the Search bar, enter group-by: reports.
Submit the search.
Expected Result:
No error occurs, and the existing From filter remains applied.
Actual Result:
The bug still reproduces in this flow (see video). In the original report, an error appeared and the From filter disappeared.
Platforms:
Windows: Chrome (others not checked)
Screenshots/Videos
Recording.3681.mp4Issue OwnerCurrent Issue Owner: @nabi-ebrahimiUpwork Automation - Do Not Edit
changed the title [-]Search - Error and From filter dropped after entering "group-by: reports" on a saved search (follow-up to #89168)[/-][+][$175] Search - Error and From filter dropped after entering "group-by: reports" on a saved search (follow-up to #89168)[/+]on Oct 17, 2026
Root cause:reports is not a valid group-by value. CONST.SEARCH.GROUP_BY (src/CONST/index.ts) only defines from, card, withdrawal-id, category, merchant, tag, day, month, week, year, quarter - there is no reports, and the parser's GROUP_BY_DEFAULT_SORT map (src/libs/SearchParser/searchParser.peggy) has no entry for it either. However, the peggy defaultFilter rule accepts any identifier as the groupBy value: updateDefaultValues("groupBy", value) stores it unvalidated, and deriveGroupByDefaults silently no-ops for unknown values. So the invalid groupBy: "reports" survives parsing into the query JSON. The #89168 fix stopped the original hard error, but in this saved-search flow the invalid value still travels through the submit/rebuild pipeline (getQueryWithUpdatedValues -> buildSearchQueryString in src/libs/SearchQueryUtils.ts), where the error resurfaces and the existing from: filter is lost from the rebuilt query.
Fix: Validate the groupBy value against CONST.SEARCH.GROUP_BY where user input enters the pipeline - in updateDefaultValues in searchParser.peggy (and/or getQueryWithUpdatedValues in src/libs/SearchQueryUtils.ts). When the value is not a recognized group-by, keep the query's previous groupBy (or omit the token) instead of carrying the invalid value forward. This keeps the existing From filter intact and avoids the error path entirely, matching the expected result.
Verification:
Follow the issue's repro steps on staging (saved expense search with a From filter, type group-by: reports, submit): confirm no error appears and the From filter chip remains applied with correct results.
Add unit tests covering group-by: reports combined with a from: filter, asserting the from filter survives the parse -> rebuild round-trip and no error is thrown.
Run the existing SearchQueryUtils and search parser test suites to check for regressions.
Please re-state the problem that we are trying to solve in this issue.
When a saved search containing an existing From filter is viewed, and the user enters group-by: reports in the search bar and submits, an error occurs and the From filter is unexpectedly dropped.
What is the root cause of that problem?
In src/libs/SearchQueryUtils.ts (inside buildSearchQueryString and query normalization when handling groupBy modifiers):
When group-by: reports is appended to an existing query string of a saved search, getSearchQueryJSON parses the query tokens. During the transition from the flat expense view to the report grouped view, the search query normalizer checks filter compatibility with groupBy.
Specifically, the from filter parameter was using direct user account identification that the report-level group parser treats as non-groupable in saved queries if not explicitly retained in the sanitized query options (SearchQueryUtils.ts:sanitizeSearchValue). Because group-by: reports forces report-level aggregation, the query sanitizer strips filters not explicitly listed in the allowed grouped filter set, leading to the parameter being dropped and an undefined field error when fetching report-grouped results.
What changes do you think we should make in order to solve the problem?
In src/libs/SearchQueryUtils.ts, ensure that when parsing and sanitizing group-by: reports, user-specified filters such as from and to are preserved across both flat and grouped search views:
In src/libs/actions/Search.ts, ensure saved search re-execution retains all active filter criteria before appending the groupBy parameter to the search URL.
What specific scenarios should we cover in automated tests?
In tests/unit/SearchQueryUtilsTest.ts:
Add a test verifying that adding group-by: reports to a search query containing from:<accountID> preserves the from filter without dropping it.
Add a test verifying that no syntax or parser error is thrown on saved search report groupings.
I worked on the original issue, #89168, and its fix in #99503. This follow-up reproduces through a different input: the top-right Search button beside Concierge, opened while viewing a saved search.
Root cause
Two behaviors combine here:
SearchRouter does not initialize its input from the saved search when searchContext.shouldShowSearchQuery is false. Submitting group-by: reports therefore starts from that text alone and loses the saved search’s from: filter before the API request.
The defaultFilter rule in src/libs/SearchParser/searchParser.peggy accepts reports as a groupBy value, although it is not a supported grouping. The resulting query reaches Search and receives 401 Invalid Query.
The original PR addressed the earlier Search input flow; it did not cover this saved-search router path.
Proposed solution
In src/components/Search/SearchRouter/SearchRouter.tsx, initialize the top-right router from the current saved search even when shouldShowSearchQuery is false. Reuse buildUserReadableQueryString and buildSubstitutionsMap so the existing filters remain visible and their readable values map back to IDs on submission.
In src/libs/SearchParser/searchParser.peggy, reject an unsupported groupBy value in defaultFilter before it updates parser defaults. Regenerate searchParser.js from the grammar. This lets getQueryWithUpdatedValues rebuild a valid query while retaining the existing from: filter.
This keeps the changes in the two paths responsible for the behavior, without adding search state or changing the API.
Edge cases and verification
Save an expense search with From = current user, view its results, open the top-right Search button beside Concierge, enter group-by: reports, and submit. The From filter should remain applied, results should load, and the invalid-search error should not appear.
Repeat with a supported grouping such as group-by: merchant. It should still apply normally.
Check a regular, unsaved search. Opening its router should retain its existing query-display behavior.
Check a query with another valid grouping followed by group-by: reports. The unsupported value should not replace the valid grouping.
Verify parser and query-rebuild tests cover unsupported grouping plus from:. Quoted "group-by: reports" should continue to behave as keyword text, since quotes change its meaning.
Activity
trentpetty commentedon Oct 16, 2026
@MelvinBot Why was I assigned a bug issue?
MelvinBot commentedon Oct 16, 2026
I assigned you because
mallenexpensifyasked for it on the original issue, [Due for payment 2026-10-01] [$250] Search - Error occurs after adding "group-by: reports" to the search query.mallenexpensifyaskedjasperhuanggto have me create an issue and assign you andnabi-ebrahimi.jasperhuanggagreed and asked me to create it.mallenexpensifysuggested.If you shouldn't be on this issue, ask
mallenexpensifyto reassign it.view run
mallenexpensify commentedon Oct 16, 2026
@nabi-ebrahimi can you comment so we can assign you? Thx
nabi-ebrahimi commentedon Oct 17, 2026
Hi @mallenexpensify, please assign me here. thanks.
melvin-bot commentedon Oct 17, 2026
Current assignee @truph01 is eligible for the External assigner, not assigning anyone new.
[-]Search - Error and From filter dropped after entering "group-by: reports" on a saved search (follow-up to #89168)[/-][+][$175] Search - Error and From filter dropped after entering "group-by: reports" on a saved search (follow-up to #89168)[/+]melvin-bot commentedon Oct 17, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022111443332056306411
khizzcr7 commentedon Oct 18, 2026
Proposal
Root cause:
reportsis not a valid group-by value.CONST.SEARCH.GROUP_BY(src/CONST/index.ts) only definesfrom,card,withdrawal-id,category,merchant,tag,day,month,week,year,quarter- there is noreports, and the parser'sGROUP_BY_DEFAULT_SORTmap (src/libs/SearchParser/searchParser.peggy) has no entry for it either. However, the peggydefaultFilterrule accepts any identifier as the groupBy value:updateDefaultValues("groupBy", value)stores it unvalidated, andderiveGroupByDefaultssilently no-ops for unknown values. So the invalidgroupBy: "reports"survives parsing into the query JSON. The #89168 fix stopped the original hard error, but in this saved-search flow the invalid value still travels through the submit/rebuild pipeline (getQueryWithUpdatedValues->buildSearchQueryStringin src/libs/SearchQueryUtils.ts), where the error resurfaces and the existingfrom:filter is lost from the rebuilt query.Fix: Validate the groupBy value against
CONST.SEARCH.GROUP_BYwhere user input enters the pipeline - inupdateDefaultValuesinsearchParser.peggy(and/orgetQueryWithUpdatedValuesinsrc/libs/SearchQueryUtils.ts). When the value is not a recognized group-by, keep the query's previous groupBy (or omit the token) instead of carrying the invalid value forward. This keeps the existing From filter intact and avoids the error path entirely, matching the expected result.Verification:
group-by: reports, submit): confirm no error appears and the From filter chip remains applied with correct results.group-by: reportscombined with afrom:filter, asserting the from filter survives the parse -> rebuild round-trip and no error is thrown.jloa-dev commentedon Oct 19, 2026
Contributor details
Your Expensify account email: jloa.dev@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~0163955a2ba22190aa
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a saved search containing an existing
Fromfilter is viewed, and the user entersgroup-by: reportsin the search bar and submits, an error occurs and theFromfilter is unexpectedly dropped.What is the root cause of that problem?
In
src/libs/SearchQueryUtils.ts(insidebuildSearchQueryStringand query normalization when handlinggroupBymodifiers):When
group-by: reportsis appended to an existing query string of a saved search,getSearchQueryJSONparses the query tokens. During the transition from the flat expense view to the report grouped view, the search query normalizer checks filter compatibility withgroupBy.Specifically, the
fromfilter parameter was using direct user account identification that the report-level group parser treats as non-groupable in saved queries if not explicitly retained in the sanitized query options (SearchQueryUtils.ts:sanitizeSearchValue). Becausegroup-by: reportsforces report-level aggregation, the query sanitizer strips filters not explicitly listed in the allowed grouped filter set, leading to the parameter being dropped and an undefined field error when fetching report-grouped results.What changes do you think we should make in order to solve the problem?
src/libs/SearchQueryUtils.ts, ensure that when parsing and sanitizinggroup-by: reports, user-specified filters such asfromandtoare preserved across both flat and grouped search views:src/libs/actions/Search.ts, ensure saved search re-execution retains all active filter criteria before appending thegroupByparameter to the search URL.What specific scenarios should we cover in automated tests?
In
tests/unit/SearchQueryUtilsTest.ts:group-by: reportsto a search query containingfrom:<accountID>preserves thefromfilter without dropping it.melvin-bot commentedon Oct 19, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
nabi-ebrahimi commentedon Oct 19, 2026
Proposal
I worked on the original issue, #89168, and its fix in #99503. This follow-up reproduces through a different input: the top-right Search button beside Concierge, opened while viewing a saved search.
Root cause
Two behaviors combine here:
SearchRouterdoes not initialize its input from the saved search whensearchContext.shouldShowSearchQueryis false. Submittinggroup-by: reportstherefore starts from that text alone and loses the saved search’sfrom:filter before the API request.defaultFilterrule insrc/libs/SearchParser/searchParser.peggyacceptsreportsas agroupByvalue, although it is not a supported grouping. The resulting query reaches Search and receives401 Invalid Query.The original PR addressed the earlier Search input flow; it did not cover this saved-search router path.
Proposed solution
src/components/Search/SearchRouter/SearchRouter.tsx, initialize the top-right router from the current saved search even whenshouldShowSearchQueryis false. ReusebuildUserReadableQueryStringandbuildSubstitutionsMapso the existing filters remain visible and their readable values map back to IDs on submission.src/libs/SearchParser/searchParser.peggy, reject an unsupportedgroupByvalue indefaultFilterbefore it updates parser defaults. RegeneratesearchParser.jsfrom the grammar. This letsgetQueryWithUpdatedValuesrebuild a valid query while retaining the existingfrom:filter.This keeps the changes in the two paths responsible for the behavior, without adding search state or changing the API.
Edge cases and verification
group-by: reports, and submit. The From filter should remain applied, results should load, and the invalid-search error should not appear.group-by: merchant. It should still apply normally.group-by: reports. The unsupported value should not replace the valid grouping.from:. Quoted"group-by: reports"should continue to behave as keyword text, since quotes change its meaning.truph01 commentedon Oct 20, 2026
@jasperhuangg Please assign @nabi-ebrahimi since they've worked on the previous issue #89168
melvin-bot commentedon Oct 20, 2026
@jasperhuangg — to hire @nabi-ebrahimi on Upwork, add the External label so a job is created and the offer is sent automatically.