App icon indicating copy to clipboard operation
App copied to clipboard

[$250] Filters - Something went wrong shown when applied Has filter in chats and changed to expense

Open lanitochka17 opened this issue 1 month ago • 18 comments

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:

  1. Sign in to ND
  2. Go to Reports tab, select Chats on LHN
  3. Click Has dropdown filter, select Link, apply - search query shows "type:chat has:link"
  4. 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

View all open jobs on GitHub

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 OwnerCurrent Issue Owner: @hoangzinh

lanitochka17 avatar Dec 13 '25 18:12 lanitochka17

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.

melvin-bot[bot] avatar Dec 13 '25 18:12 melvin-bot[bot]

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:

  1. Sign in to ND
  2. Go to Reports tab, select Chats on LHN
  3. Click Has dropdown filter, select Link, apply - search query shows "type:chat has:link"
  4. 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: Only link and attachment are valid
  • For type:expense: Only receipt, attachment, tag, category are 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:

  1. User applies type:chat has:link filter
  2. User changes type to Expense via dropdown
  3. Code checks if has filter KEY is allowed for Expense type → Yes (it's in ALLOWED_TYPE_FILTERS)
  4. Code does NOT check if link VALUE is valid for Expense type
  5. Invalid query type:expense has:link is sent to backend
  6. Backend cannot process has:link for 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

abbasifaizan70 avatar Dec 13 '25 18:12 abbasifaizan70

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.

daledah avatar Dec 13 '25 19:12 daledah

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.

lorretheboy avatar Dec 14 '25 12:12 lorretheboy

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

tsa321 avatar Dec 15 '25 13:12 tsa321

I can't repro on iOS v 9.2.78-2 or Android v9.2.75-0. Requesting retest.

RachCHopkins avatar Dec 16 '25 04:12 RachCHopkins

@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

daledah avatar Dec 16 '25 04:12 daledah

Cool, thank makes sense, thanks @daledah !

RachCHopkins avatar Dec 16 '25 04:12 RachCHopkins

Job added to Upwork: https://www.upwork.com/jobs/~022000784836527589969

melvin-bot[bot] avatar Dec 16 '25 04:12 melvin-bot[bot]

Triggered auto assignment to Contributor-plus team member for initial proposal review - @hoangzinh (External)

melvin-bot[bot] avatar Dec 16 '25 04:12 melvin-bot[bot]

Thanks for posting proposals, everyone. I will try to review them tomorrow

hoangzinh avatar Dec 18 '25 16:12 hoangzinh

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 avatar Dec 19 '25 10:12 hoangzinh

@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?

daledah avatar Dec 19 '25 11:12 daledah

@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

abbasifaizan70 avatar Dec 19 '25 11:12 abbasifaizan70

@hoangzinh Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

melvin-bot[bot] avatar Dec 23 '25 00:12 melvin-bot[bot]

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

melvin-bot[bot] avatar Dec 23 '25 16:12 melvin-bot[bot]

@hoangzinh Huh... This is 4 days overdue. Who can take care of this?

melvin-bot[bot] avatar Dec 24 '25 23:12 melvin-bot[bot]

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 avatar Dec 25 '25 15:12 hoangzinh

@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!

melvin-bot[bot] avatar Dec 27 '25 21:12 melvin-bot[bot]

@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:

  1. We don't have the key HAS_NOT in 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, ],

  1. 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

  1. We don't need to make any changes in the applyChanges function; we just need to handle it in the updateFilterForm.

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.

daledah avatar Dec 29 '25 14:12 daledah

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

melvin-bot[bot] avatar Dec 30 '25 16:12 melvin-bot[bot]

@hoangzinh 6 days overdue. This is scarier than being forced to listen to Vogon poetry!

melvin-bot[bot] avatar Jan 01 '26 23:01 melvin-bot[bot]

Issue not reproducible during KI retests. (First week)

mvtglobally avatar Jan 04 '26 20:01 mvtglobally

@hoangzinh 10 days overdue. I'm getting more depressed than Marvin.

melvin-bot[bot] avatar Jan 06 '26 00:01 melvin-bot[bot]

Back on this issue today

hoangzinh avatar Jan 06 '26 08:01 hoangzinh

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

melvin-bot[bot] avatar Jan 06 '26 16:01 melvin-bot[bot]