[$250] Filters - Something went wrong shown when applied Has filter in chats and changed to expense
If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!
Version Number: 9.2.78-0 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack: N/A 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.1, MacBook Air Chrome App Component: Search
Action Performed:
- Sign in to ND
- Go to Reports tab, select Chats on LHN
- Click Has dropdown filter, select Link, apply - search query shows "type:chat has:link"
- Click Type dropdown filter, select Expense, apply - search query shows "type:expense has:link", "Something went wrong" page appears
Expected Result:
"Something went wrong" page is not shown
Actual Result:
"Something went wrong" page is shown when applied filter in chats and changed type to expense. The search query shows type:expense has:link. The same issue happens, if apply Has filter for expense and change the type dropdown to chat
Workaround:
Unknown
Platforms:
- [x] Android: App
- [ ] Android: mWeb Chrome
- [x] iOS: App
- [ ] iOS: mWeb Safari
- [ ] iOS: mWeb Chrome
- [x] Windows: Chrome
- [ ] MacOS: Chrome / Safari
Screenshots/Videos
https://github.com/user-attachments/assets/95e55d2c-4701-410d-9340-3aeca33d54a7
Upwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~022000784836527589969
- Upwork Job ID: 2000784836527589969
- Last Price Increase: 2025-12-23
Issue Owner
Current Issue Owner: @hoangzinh
Triggered auto assignment to @RachCHopkins (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.
Proposal
Please re-state the problem that we are trying to solve in this issue.
When switching search type (e.g., from Chat to Expense) with a Has filter applied, a "Something went wrong" error page appears. This happens because certain Has filter values are type-specific and become invalid when the type changes.
Steps to Reproduce:
- Sign in to ND
- Go to Reports tab, select Chats on LHN
- Click Has dropdown filter, select Link, apply - search query shows "type:chat has:link"
- Click Type dropdown filter, select Expense, apply - search query shows "type:expense has:link"
Expected Result: "Something went wrong" page is not shown. The invalid has:link filter should be automatically removed when switching to Expense type.
Actual Result: "Something went wrong" page is shown when applied filter in chats and changed type to expense. The search query shows type:expense has:link. The same issue happens if you apply Has filter for expense and change the type dropdown to chat.
What is the root cause of that problem?
The root cause is that when switching search type, the has filter values are not being properly filtered based on type-specific validity.
Type-Specific Has Values:
The has filter values are type-specific as defined in getHasOptions() function in SearchUIUtils.ts:
function getHasOptions(translate: LocalizedTranslate, type: SearchDataTypes) {
switch (type) {
case CONST.SEARCH.DATA_TYPES.EXPENSE:
return [
{text: translate('common.receipt'), value: CONST.SEARCH.HAS_VALUES.RECEIPT},
{text: translate('common.attachment'), value: CONST.SEARCH.HAS_VALUES.ATTACHMENT},
{text: translate('common.tag'), value: CONST.SEARCH.HAS_VALUES.TAG},
{text: translate('common.category'), value: CONST.SEARCH.HAS_VALUES.CATEGORY},
];
case CONST.SEARCH.DATA_TYPES.CHAT:
return [
{text: translate('common.link'), value: CONST.SEARCH.HAS_VALUES.LINK},
{text: translate('common.attachment'), value: CONST.SEARCH.HAS_VALUES.ATTACHMENT},
];
default:
return [];
}
}
- For
type:chat: Onlylinkandattachmentare valid - For
type:expense: Onlyreceipt,attachment,tag,categoryare valid
Filter Key vs Filter Value Validation:
The existing ALLOWED_TYPE_FILTERS in SearchAdvancedFiltersForm.ts only validates that the has filter key is allowed for both types, but it doesn't validate the values within the filter:
[CONST.SEARCH.DATA_TYPES.EXPENSE]: [
// ...
FILTER_KEYS.HAS, // Key is allowed
FILTER_KEYS.HAS_NOT,
],
[CONST.SEARCH.DATA_TYPES.CHAT]: [
// ...
FILTER_KEYS.HAS, // Key is allowed
FILTER_KEYS.HAS_NOT,
],
Missing Value Cleanup on Type Change:
When the type changes, only status and groupBy are reset, but has values are NOT filtered:
In SearchFiltersBar.tsx (updateFilterForm):
// If the type has changed, reset the status so we dont have an invalid status selected
if (updatedFilterFormValues.type !== filterFormValues.type) {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
}
// ❌ No cleanup of incompatible 'has' values
In SearchFiltersTypePage.tsx (applyChanges):
const applyChanges = useCallback(() => {
const hasTypeChanged = selectedItem !== searchAdvancedFiltersForm?.type;
const updatedFilters = {
type: selectedItem,
...(hasTypeChanged && {
groupBy: null,
status: CONST.SEARCH.STATUS.EXPENSE.ALL,
}),
// ❌ No cleanup of incompatible 'has' values
};
updateAdvancedFilters(updatedFilters);
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute());
}, [searchAdvancedFiltersForm?.type, selectedItem]);
Why the bug occurs:
- User applies
type:chat has:linkfilter - User changes type to Expense via dropdown
- Code checks if
hasfilter KEY is allowed for Expense type → Yes (it's inALLOWED_TYPE_FILTERS) - Code does NOT check if
linkVALUE is valid for Expense type - Invalid query
type:expense has:linkis sent to backend - Backend cannot process
has:linkfor expense type → "Something went wrong" error
What changes do you think we should make in order to solve the problem?
Filter out incompatible has values when the type changes using the getHasOptions() function.
File: App/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx
Update the updateFilterForm callback to filter incompatible has values when type changes:
const updateFilterForm = useCallback(
(values: Partial<SearchAdvancedFiltersForm>) => {
const updatedFilterFormValues: Partial<SearchAdvancedFiltersForm> = {
...filterFormValues,
...values,
};
// If the type has changed, reset the status so we dont have an invalid status selected
if (updatedFilterFormValues.type !== filterFormValues.type) {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
// Filter out incompatible 'has' values for the new type
const newType = updatedFilterFormValues.type ?? CONST.SEARCH.DATA_TYPES.EXPENSE;
const validHasOptions = getHasOptions(translate, newType).map((option) => option.value);
if (updatedFilterFormValues.has) {
updatedFilterFormValues.has = updatedFilterFormValues.has.filter((hasValue) =>
validHasOptions.includes(hasValue)
);
// Clear has if no valid values remain
if (updatedFilterFormValues.has.length === 0) {
updatedFilterFormValues.has = undefined;
}
}
// Clear 'is' filter if switching away from chat type (is is chat-only)
if (newType !== CONST.SEARCH.DATA_TYPES.CHAT && updatedFilterFormValues.is) {
updatedFilterFormValues.is = undefined;
}
}
const queryString = buildQueryStringFromFilterFormValues(updatedFilterFormValues);
close(() => {
Navigation.setParams({q: queryString, rawQuery: undefined});
});
},
[filterFormValues, translate],
);
Note: Need to add translate to the dependency array.
File: App/src/pages/Search/SearchAdvancedFiltersPage/SearchFiltersTypePage.tsx
Add import and update the applyChanges callback:
// Add import at the top
import {getHasOptions, getTypeOptions} from '@libs/SearchUIUtils';
// Update the applyChanges callback
const applyChanges = useCallback(() => {
const hasTypeChanged = selectedItem !== searchAdvancedFiltersForm?.type;
// Calculate which 'has' values are valid for the new type
let filteredHasValues: string[] | null = null;
if (hasTypeChanged && searchAdvancedFiltersForm?.has) {
const validHasOptions = getHasOptions(translate, selectedItem).map((option) => option.value);
const validValues = searchAdvancedFiltersForm.has.filter((hasValue) =>
validHasOptions.includes(hasValue)
);
filteredHasValues = validValues.length > 0 ? validValues : null;
}
const updatedFilters = {
type: selectedItem,
...(hasTypeChanged && {
groupBy: null,
status: CONST.SEARCH.STATUS.EXPENSE.ALL,
has: filteredHasValues,
// Clear 'is' filter if not switching to chat type (is is chat-only)
...(selectedItem !== CONST.SEARCH.DATA_TYPES.CHAT && {is: null}),
}),
};
updateAdvancedFilters(updatedFilters);
Navigation.goBack(ROUTES.SEARCH_ADVANCED_FILTERS.getRoute());
}, [searchAdvancedFiltersForm?.type, searchAdvancedFiltersForm?.has, selectedItem, translate]);
What alternative solutions did you explore? (Optional)
None
Proposal
Please re-state the problem that we are trying to solve in this issue.
"Something went wrong" page is shown when applied filter in chats and changed type to expense. The search query shows type:expense has:link. The same issue happens, if apply Has filter for expense and change the type dropdown to chat
What is the root cause of that problem?
When switching from CHAT to EXPENSE with "has:link" applied, "link" is invalid for EXPENSE
The query string still contained the invalid filter, causing an error
https://github.com/Expensify/App/blob/8cd91dce98b21ad3d8d537ed453e051437fa7dc8/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx#L310-L319
What changes do you think we should make in order to solve the problem?
we should update updateFilterForm in SearchFiltersBar.tsx to filter out invalid "has" values when the type changes by
- Gets valid "has" options for the new type using getHasOptions
- Filters existing "has" values to only include those valid for the new type
- Clears the "has" filter if no valid values remain
if (updatedFilterFormValues.type !== filterFormValues.type) {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
// Filter out invalid "has" values for the new type
if (updatedFilterFormValues.has && updatedFilterFormValues.type) {
const validHasOptions = getHasOptions(translate, updatedFilterFormValues.type);
const validHasValues = new Set(validHasOptions.map((option) => option.value));
const filteredHasValues = updatedFilterFormValues.has.filter((hasValue) => validHasValues.has(hasValue as ValueOf<typeof CONST.SEARCH.HAS_VALUES>));
updatedFilterFormValues.has = filteredHasValues.length > 0 ? filteredHasValues : undefined;
}
}
https://github.com/Expensify/App/blob/8cd91dce98b21ad3d8d537ed453e051437fa7dc8/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx#L310-L319
Note: we should do the same for other filter options if its have the same problem
Result:
https://github.com/user-attachments/assets/254a5ec5-9463-4d0e-8172-7be455d15373
What alternative solutions did you explore? (Optional)
Or we can simply update the updatedFilterFormValues.has to undefined if the type is changed
if (updatedFilterFormValues.type !== filterFormValues.type) {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
updatedFilterFormValues.has = undefined;
}
Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.
Proposal
Please re-state the problem that we are trying to solve in this issue.
Filters - Something went wrong shown when applied Has filter in chats and changed to expense
What is the root cause of that problem?
When switching between search types, the code was always setting status = CONST.SEARCH.STATUS.EXPENSE.ALL, even when switching to the CHAT type. However, CHAT type doesn't support the status filter at all (as defined in ALLOWED_TYPE_FILTERS). This caused the query builder to generate an invalid query string with a status filter for CHAT type, which is not supported
https://github.com/Expensify/App/blob/92dec2d18e1d5c33774b5a0ec0868127a0205ec9/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx#L309-L314
What changes do you think we should make in order to solve the problem?
We only set status for types that support it (CHAT doesn't have status)
// If the type has changed, reset the status so we dont have an invalid status selected
if (updatedFilterFormValues.type !== filterFormValues.type) {
const newType = updatedFilterFormValues.type ?? CONST.SEARCH.DATA_TYPES.EXPENSE;
if (newType === CONST.SEARCH.DATA_TYPES.CHAT) {
updatedFilterFormValues.status = undefined;
} else {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
}
}
https://github.com/Expensify/App/blob/92dec2d18e1d5c33774b5a0ec0868127a0205ec9/src/components/Search/SearchPageHeader/SearchFiltersBar.tsx#L309-L312
What alternative solutions did you explore? (Optional)
if (updatedFilterFormValues.type !== filterFormValues.type) {
const newType = updatedFilterFormValues.type ?? CONST.SEARCH.DATA_TYPES.EXPENSE;
// Only set status for types that support it (e.g., CHAT doesn't have status)
if (isFilterSupported(FILTER_KEYS.STATUS, newType)) {
updatedFilterFormValues.status = CONST.SEARCH.STATUS.EXPENSE.ALL;
} else {
updatedFilterFormValues.status = undefined;
}
}
Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.
I think this is a BE error. You can reproduce the issue by directly editing the text in the text box:
https://github.com/user-attachments/assets/a7b08002-6c73-436c-9b50-c8990fd8c843
I can't repro on iOS v 9.2.78-2 or Android v9.2.75-0. Requesting retest.
@RachCHopkins It seems the "Something went wrong" page bug has been fixed. However, there's still one more issue: when changing from type:chat to type:expense, has:link is not removed from the search query (because the has field in type expense doesn't have link option). We should remove the has:link query in the same way we do with is:. Please see this video:
https://github.com/user-attachments/assets/53d7524d-6078-475c-8982-5d6b09ace4ec
Cool, thank makes sense, thanks @daledah !
Job added to Upwork: https://www.upwork.com/jobs/~022000784836527589969
Triggered auto assignment to Contributor-plus team member for initial proposal review - @hoangzinh (External)
Thanks for posting proposals, everyone. I will try to review them tomorrow
It seems @abbasifaizan70 and @daledah have the correct root cause, and their approach is almost the same. However, since @abbasifaizan70 is first, I think his proposal looks good to me.
@hoangzinh The selected proposal contains many redundant and unclear parts (it even includes some incorrect information), and it looks like it was generated by AI. Could you please review it again?
@hoangzinh, my suggested solution is working fine. I have tested that here is the demo
https://github.com/user-attachments/assets/ed780fbc-fd8a-47e9-8ac9-b9b677e82b9b
@hoangzinh Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸
@hoangzinh Huh... This is 4 days overdue. Who can take care of this?
The selected proposal contains many redundant and unclear parts (it even includes some incorrect information)
@daledah can you give more details on it? I know the implementation might be incorrect, but the idea is correct to me "Filter out incompatible has values when the type changes using the getHasOptions() function."
@hoangzinh @RachCHopkins this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!
@daledah can you give more details on it? I know the implementation might be incorrect, but the idea is correct to me "Filter out incompatible has values when the type changes using the getHasOptions() function."
@hoangzinh Here are some issues with the selected proposal:
- We don't have the key
HAS_NOTin filter keys
// ... FILTER_KEYS.HAS, // Key is allowed FILTER_KEYS.HAS_NOT, ], [CONST.SEARCH.DATA_TYPES.CHAT]: [ // ... FILTER_KEYS.HAS, // Key is allowed FILTER_KEYS.HAS_NOT, ],
- In
Why the bug occurs:part, this information is incorrect because the backend is still accepting and returning an empty result instead of an error more infor in this comment
Backend cannot process has:link for expense type → "Something went wrong" error
- We don't need to make any changes in the
applyChangesfunction; we just need to handle it in theupdateFilterForm.
And there's another problem: the selected proposal doesn't have any permalinks. I think we should use permalinks instead of writing a function like that.
📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸
@hoangzinh 6 days overdue. This is scarier than being forced to listen to Vogon poetry!
Issue not reproducible during KI retests. (First week)
@hoangzinh 10 days overdue. I'm getting more depressed than Marvin.
Back on this issue today
📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸