Skip to content

[$175] Search-"type:expense vendor name", vendor name not highlighted and search result not correct #105375

Open

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


Version Number: v9.5.25-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: https://test-management.browserstack.com/projects/2219752/test-runs/81167661/folder/13176908/145045748/1923344198?sort=-identifier
Email or phone of affected tester (no customers): [email protected]
Issue reported by: Applause Internal Team
Bug source: Regression TC Execution
Device used: Win 11/Chrome
App Component: Search

Action Performed:

Precondition:

  • User is an admin of a workspace connected to QBO with Export company card expenses as set to Credit card
  • Go to the workspace chat create 2 non-reimbursable expenses: one with vendor "A B" and the other with vendor "Admin Denis"
  1. Open https://staging.new.expensify.com
  2. Navigate to Spend > Expenses
  3. Click the search icon at the top right and type "type:expense vendor:A B" > search
  4. Clear the search and type: "type:expense vendor A B" > search
  5. Clear the search and type: "type:expense vendor Admin Denis" > search

Expected Result:

Click the search icon at the top right and type type:expense vendor followed by the vendor name, verify the vendor name is highlighted like the other filter values. The search results match the filtered results

Actual Result:

Typing "type:expense vendor:A B" only the "A" is highlighted on the search box and displays no search result. Typing "type:expense vendor A B", the vendor name isn't highlighted, result displays both expenses with vendor "A B" and vendor "Admin Denis". Typing "type:expense vendor Admin Denis" displays no search result.

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome Safari

Screenshots/Videos

Bug7299536_1793267750229.Recording__2645.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Julia Ponikarchuk
Bug ID: 7299536

Upwork Automation - Do Not Edit
Issue OwnerCurrent Issue Owner: @jasperhuangg

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Oct 29, 2026
  2. applause-bot commented on Oct 29, 2026

    @applause-bot
    Author

    While Applause doesn't embed attachments for this bug, here are the links for quick view:
    https://platform.applause.com/services/links/v1/external/d8eba1b8c483cf692fdae1f7eb9d1964794b7c8664a6436dad1b3942aa26e821

  3. MelvinBot commented on Oct 29, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    This is mostly expected search syntax, plus one missing feature in the search box:

    1. vendor:A B (step 3). An unquoted value ends at the first space. This is true for every filter, not only vendor (baseRules.peggy:221). The query becomes vendor:A plus the keyword B, so only "A" is highlighted and nothing matches. Multi-word values must be quoted, for example vendor:"A B". Merchant, category, and tag work the same way.
    2. vendor A B and vendor Admin Denis (steps 4 and 5). Without a colon, these are plain keyword searches. The App sends the words to the server unchanged, and the server decides what matches. Nothing is highlighted because there is no filter.
    3. The App gap. Typing vendor: in the search box suggests no vendors. The suggestion switch handles tag and category, but has no VENDOR case (useAutocompleteSuggestions.ts:186-203). For other filters, picking a suggestion adds the quotes automatically (SearchRouter.tsx:476). For vendor, you have to type the quotes yourself, and the test step didn't include them.

    What changes do you think we should make in order to solve the problem?

    1. Add a VENDOR case to useAutocompleteSuggestions.ts. Fill it from the same synced vendor list that the Filters > Vendor picker uses. Selecting a vendor then inserts vendor:"A B" and highlights the full name.
    2. Update the regression test case to use vendor:"<name>" for names that contain spaces.

    Steps 4 and 5 (keyword results) are decided by the server. If those results are wrong, an internal engineer needs to look at them.

    What alternative solutions did you explore? (Optional)

    Let vendor: accept unquoted values that contain spaces. I rejected this because it would break the rule every other filter follows. It would also make vendor:A B ambiguous with a following keyword.

    Supporting evidence

    Possibly related: [$175] Filters - Vendor option is missing in filters


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/105375) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Oct 29, 2026
  5. melvin-bot commented on Oct 29, 2026

    @melvin-bot

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

  6. changed the title [-]Search-"type:expense vendor name", vendor name not highlighted and search result not correct[/-] [+][$175] Search-"type:expense vendor name", vendor name not highlighted and search result not correct[/+] on Oct 29, 2026
  7. melvin-bot commented on Oct 29, 2026

    @melvin-bot
  8. camesenin commented on Oct 29, 2026

    @camesenin

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    The reported searches do not highlight or return the vendor names as the tester expects. However, the report mixes explicit filter syntax (vendor:A B) with plain text (vendor A B), so the expected behavior must be checked against the supported search syntax before classifying this as a code regression.

    Verdict: MelvinBot’s proposal is incomplete as a fix for the reported reproduction steps. No independently verified replacement patch is established.

    What is the root cause of that problem?

    The supplied proposal describes three distinct behaviors:

    Query Melvin’s interpretation Evidence status
    type:expense vendor:A B Vendor filter A, plus keyword B Consistent with the quoted grammar description; not independently executed
    type:expense vendor A B Plain keywords after the expense filter Not independently executed
    type:expense vendor Admin Denis Plain keywords after the expense filter Not independently executed

    Melvin cites these implementation locations:

    • src/libs/SearchParser/baseRules.peggy:221,231–238: unquoted and quoted value boundaries.
    • src/hooks/useAutocompleteSuggestions.ts:186–203: suggestion dispatch.
    • src/components/Search/SearchRouter/SearchRouter.tsx:476: suggestion insertion.
    • src/libs/SearchQueryUtils.ts:2016–2019: vendor query handling.

    These are references supplied by Melvin, not source locations independently verified during this audit. The pinned files and target issue could not be retrieved, no Expensify checkout exists in this workspace, and no parser, Jest, or browser reproduction was run.

    Consequently:

    • A parser defect is not established.
    • A backend matching defect is not established.
    • Missing vendor autocomplete remains a source-verification hypothesis.
    • No security impact is demonstrated.

    What changes do you think we should make in order to solve the problem?

    Do not approve an implementation from the current evidence. First establish whether the supported baseline, type:expense vendor:"A B", highlights the complete name and returns the intended expense in the stated QBO configuration.

    Then use the smallest change supported by that result:

    1. Quoted syntax works: correct the regression procedure. Treat autocomplete as a separately scoped usability improvement unless the issue owner explicitly accepts it as this issue’s resolution.
    2. Quoted syntax fails: capture the parsed query and outgoing Search request, then locate the first incorrect transformation. Patch that boundary.
    3. Vendor suggestions are the accepted scope: verify the existing picker’s eligibility, loading, and serialization behavior before reusing its data.

    An autocomplete patch must account for vendor data arriving after the hook mounts, workspace changes, duplicate names across workspaces, empty results, and values containing punctuation or quotes. These are required verification cases; they are not proven failures in existing code.

    No TypeScript diff is included because the actual types, helpers, dependencies, and insertion logic were unavailable. Inventing those details would fail the requirement for an exact, production-reviewable patch.

    What alternative solutions did you explore? (Optional)

    MelvinBot’s central coverage failure: adding suggestions only changes behavior when a user selects a suggestion. It does not change any of the three reported queries when typed and submitted unchanged. Updating the test to use quotes changes the reproduction procedure; it does not prove the original behavior was fixed.

    Melvin’s syntax explanation may nevertheless be correct. Rewriting the parser to absorb spaces would introduce ambiguity between vendor values and subsequent keywords without an established syntax contract.

    Likewise, adding a colon automatically to vendor Admin Denis could reinterpret legitimate keyword searches.

    The related vendor-filter issue documents earlier removal and restoration work. That warrants an overlap check; it does not establish duplication of this autocomplete issue.

    A claim that this is “the only safe solution” is unsupported. Expensify’s contribution guidelines require reproduction before proposing a fix and meaningful differentiation from existing proposals.

    What automated tests can you add to prevent this from re-occurring?

    Once the source is available, add focused Jest coverage:

    • Parser: verify that vendor:"A B" preserves one complete vendor value and creates no stray B keyword.
    • Syntax compatibility: verify the existing interpretation of vendor:A B and colonless queries.
    • Autocomplete, if implemented: mount before vendor data arrives, update the mocked subscription, and assert that suggestions refresh.
    • Insertion: select A B with the caret inside an existing query; assert that the inserted value parses back to exactly A B while neighboring filters and keywords remain intact.
    • Serialization: cover quoted and punctuated names through the existing insertion/parser round trip.
    • Workspace changes: ensure suggestions follow the same eligibility and data-selection rules as the vendor picker.

    Mocked Jest responses cannot establish backend result correctness. That requires a controlled reproduction with the two stated expenses and captured request/result evidence.

    Execution status: none of these tests were run; no passing validation is claimed.

    Contributor details
    Your Expensify account email: [email protected]
    Upwork Profile Link: https://www.upwork.com/freelancers/~01b60d704ba377be83

  9. melvin-bot commented on Oct 29, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  10. khizzcr7 commented on Oct 29, 2026

    @khizzcr7

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    Searching type:expense vendor:A B highlights only "A" and returns nothing, and colonless searches like type:expense vendor Admin Denis highlight nothing and return wrong results. The tester expects the vendor name to be highlighted like other filter values and the results to match the filtered expenses.

    What is the root cause of that problem?

    Two distinct behaviors, one of which is a genuine App gap:

    1. Unquoted multi-word values end at the first space. This is the search grammar working as designed, not a vendor-specific regression: vendor:A B parses as vendor A plus keyword B, so only "A" is highlighted and nothing matches both conditions. Multi-word vendor values must be quoted, e.g. vendor:"A B". Without a colon, vendor A B and vendor Admin Denis are plain keyword searches, so nothing is highlighted and the server decides what matches.
    2. The App gap: typing vendor: suggests no vendors. Verified against main source: the switch in src/hooks/useAutocompleteSuggestions.ts covers TAG, CATEGORY, CURRENCY, TAX_RATE and user filters, but has no vendor case. So a user can never discover the correct quoted form from the search box, which is exactly why the tester typed the unquoted forms.

    What changes do you think we should make in order to solve the problem?

    Add a vendor case to useAutocompleteSuggestions.ts, mirroring the existing TAG/CATEGORY cases:

    • Source the list from the workspace vendor list already synced for expenses, filtered by the typed substring (case-insensitive), capped at 10, and excluding vendor values already used in the current query (the same alreadyAutocompletedKeys dedup the TAG case uses).
    • Reuse the existing insertion path unchanged: sanitizeSearchValue in src/libs/SearchQueryUtils.ts already wraps any value containing a space in quotes, so selecting "A B" inserts vendor:"A B", which parses as a single vendor value and highlights in full.
    • Handle the lazy-load case explicitly: vendor lists load lazily, so while the list is not yet synced show no suggestions rather than stale data, following the category case's loader pattern. No parser change: the grammar already handles quoted values correctly, and rewriting it to absorb spaces would create ambiguity between vendor values and following keywords.

    What alternative solutions did you explore?

    • Parser change to absorb spaces into the vendor value: rejected, it would make vendor:A B ambiguous with a vendor filter plus a keyword, and would change behavior for every filter.
    • Auto-inserting a colon after vendor: rejected, it would reinterpret legitimate keyword searches.
    • Backend/server matching change: not needed; the server already matches quoted vendor values by text.

    What automated tests can you add to prevent this from re-occurring?

    • Jest test for the new vendor case: typing vendor: with a synced vendor list returns matching vendors; with no synced list returns an empty list.
    • Insertion test: selecting "A B" produces vendor:"A B" and reparsing yields exactly one vendor value A B, with neighbouring filters intact.
    • Dedup test: a vendor already used in the query is not suggested again.
    • Regression test for the grammar: unquoted vendor:A B still parses as vendor A plus keyword B (documented behaviour, unchanged).
  11. sonofkratos92 commented on Oct 29, 2026

    @sonofkratos92

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When searching in Spend > Expenses for expenses with multi-word vendor names like "A B" or "Admin Denis":

    1. Typing type:expense vendor:A B only highlights A in the search input, shows no vendor autocomplete suggestions to select "A B", and returns no results (because A is parsed as the vendor filter value and B is parsed as a separate free-text keyword).
    2. Typing type:expense vendor A B or type:expense vendor Admin Denis without a colon runs a plain keyword search rather than a vendor: filter.

    What is the root cause of that problem?

    1. In the search grammar (src/libs/SearchParser/baseRules.peggy:221), unquoted filter values terminate at whitespace, so multi-word filter values must be quoted (vendor:"A B"). When a user types vendor:A and then presses space followed by B (type:expense vendor:A B), defaultFilter in src/libs/SearchParser/autocompleteParser.peggy:39-75 parses vendor:A into ranges and freeTextFilter (autocompleteParser.peggy:83) parses B and resets autocomplete = null.
    2. src/hooks/useAutocompleteSuggestions.ts has no case CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR: branch, so typing vendor: never suggests synced workspace vendors (unlike category:, tag:, or exported-to:), which is what normally wraps multi-word values in quotes via sanitizeSearchValue in src/components/Search/SearchRouter/SearchRouter.tsx:480.
    3. Even if a VENDOR case is added to useAutocompleteSuggestions.ts alone, CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR is also missing from CONTINUATION_DETECTION_SEARCH_FILTER_KEYS in src/libs/SearchAutocompleteUtils.ts:683-691. Without VENDOR in CONTINUATION_DETECTION_SEARCH_FILTER_KEYS (and mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR on each suggestion item):
      • As soon as the user types a space in a multi-word vendor name (type:expense vendor:A B or type:expense vendor:Admin Denis), parseForAutocomplete returns autocomplete: null. Because useAutocompleteSuggestions.ts:157-168 only checks CONTINUATION_DETECTION_SEARCH_FILTER_KEYS when !autocomplete, the vendor suggestions disappear immediately after the space is typed.
      • When selecting a suggestion after typing a space, getTrimmedUserSearchQueryPreservingComma (src/libs/SearchAutocompleteUtils.ts:1045-1071) only trims back to vendor: (or the last comma) when fieldKey is in CONTINUATION_DETECTION_SEARCH_FILTER_KEYS; otherwise it falls back to getQueryWithoutAutocompletedPart(textInputValue), which returns the untrimmed string when parsedQuery.autocomplete is null and produces type:expense vendor:A B"A B".

    What changes do you think we should make in order to solve the problem?

    1. In src/libs/SearchAutocompleteUtils.ts, add CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR to CONTINUATION_DETECTION_SEARCH_FILTER_KEYS:
    const CONTINUATION_DETECTION_SEARCH_FILTER_KEYS: SearchFilterKey[] = [
        CONST.SEARCH.SYNTAX_FILTER_KEYS.TO,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.ASSIGNEE,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.PAYER,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID_BY,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTER,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.ATTENDEE,
        CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR,
    ];
    1. In src/hooks/useAutocompleteSuggestions.ts, import usePermissions from ./usePermissions and getMatchingVendors, getVendorFeaturePolicyIDs from @libs/PolicyUtils, check const isVendorMatchingBetaEnabled = isBetaEnabled(CONST.BETAS.VENDOR_MATCHING);, and add the VENDOR case to switch (autocompleteKey) using the same synced vendor source as VendorSelector.tsx with mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR:
            case CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR: {
                const uniqueVendorNames = new Set<string>(
                    getVendorFeaturePolicyIDs(policies, isVendorMatchingBetaEnabled).flatMap((id) =>
                        getMatchingVendors(policies?.[`${ONYXKEYS.COLLECTION.POLICY}${id}`]).map((vendor) => vendor.name),
                    ),
                );
                const filteredVendors = Array.from(uniqueVendorNames)
                    .filter((vendorName) => {
                        if (!vendorName) {
                            return false;
                        }
                        const lowerVendorName = vendorName.toLowerCase();
                        return lowerVendorName.includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.has(lowerVendorName);
                    })
                    .sort((a, b) => localeCompare(a, b))
                    .slice(0, 10);
    
                return filteredVendors.map((vendorName) => ({
                    filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.VENDOR,
                    text: vendorName,
                    mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR,
                }));
            }

    With both changes in place, typing type:expense vendor:A B or type:expense vendor:Admin Denis keeps the matching vendor suggestion visible across spaces, and selecting it replaces the unquoted text after vendor: with vendor:"A B" (or vendor:"Admin Denis"), highlighting the full vendor name and returning the matching expense.

    What alternative solutions did you explore? (Optional)

    Changing the Peggy parser to allow unquoted spaces inside vendor: values would make vendor:A B ambiguous with trailing search keywords and break consistency with other filters (merchant:, category:, tag:). Adding VENDOR to useAutocompleteSuggestions together with CONTINUATION_DETECTION_SEARCH_FILTER_KEYS preserves the standard quoted syntax while supporting multi-word vendor autocomplete when typing spaces.

  12. apoorv-xs commented on Oct 29, 2026

    @apoorv-xs

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When typing a vendor filter in the global Search bar (such as type:expense vendor:A B or type:expense vendor Admin Denis), multi-word vendor names containing spaces are not highlighted correctly (only the initial token preceding the space is highlighted in SearchAutocompleteInput), and running the search results in "Nothing to show" instead of returning the matching expenses. Additionally, typing vendor: does not display any autocomplete suggestions for vendors from connected workspace policies, forcing the user to type vendor names manually without quotes.

    What is the root cause of that problem?

    1. Missing Vendor Autocomplete Suggestions:
      In src/hooks/useAutocompleteSuggestions.ts, switch (autocompleteKey) provides autocomplete handlers for TAG, CATEGORY, CURRENCY, TAX_RATE, FROM, TO, STATUS, IN, ACTION, etc., but completely lacks a handler for CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR. When a user types vendor:, the hook returns an empty array, so no vendor suggestions appear.
    2. Missing Multi-Word Continuation Detection for Vendor:
      In src/libs/SearchAutocompleteUtils.ts, CONTINUATION_DETECTION_SEARCH_FILTER_KEYS contains [TO, FROM, ASSIGNEE, PAYER, PAID_BY, EXPORTER, ATTENDEE], but does not include CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR. When a user types a multi-word vendor name separated by a space (e.g. vendor:Admin Denis), the parser treats the trailing word as a free-text token. Because VENDOR is absent from CONTINUATION_DETECTION_SEARCH_FILTER_KEYS, useAutocompleteSuggestions does not bridge the space continuation, dropping autocomplete entirely.
    3. Syntax Highlighting and Lexer Delimitation for Unquoted Values:
      In src/libs/SearchParser/autocompleteParser.peggy and searchParser.peggy, unquoted filter values are parsed by alphanumeric = chars:(escapedChar / [^ ,\t\n\r\xA0])+, which delimits tokens by whitespace. In Expensify's Search syntax, values containing whitespace must be wrapped in double quotes (e.g. vendor:"A B"). When a user selects a suggestion from the autocomplete dropdown, SearchRouter.tsx automatically invokes sanitizeSearchValue(item.searchQuery), which wraps multi-word strings in double quotes. Because vendor autocomplete was not implemented, users were forced to type the vendor name manually without quotes. This caused autocompleteParser to highlight only the first word (e.g. "A" in "A B", leaving "B" unhighlighted) and caused searchParser to interpret the trailing words as disjoint keyword filters (vendor: "A" AND keyword: "B"), leading to zero matching search results.
    4. Missing Live Markdown Shared Value for Vendor:
      In src/components/Search/SearchAutocompleteInput.tsx, shared values are provisioned and synchronized on the live markdown runtime for categories, tags, currencies, logins, and exportedTo destinations, but no vendorSharedValue is provided to parseForLiveMarkdown for vendor name validation.

    What changes do you think we should make in order to solve the problem?

    1. In src/libs/SearchAutocompleteUtils.ts:

      • Add getAutocompleteVendors to extract unique vendor names across accessible policies (using getVendorFeaturePolicyIDs and getMatchingVendors from @libs/PolicyUtils):
      function getAutocompleteVendors(allPolicies: OnyxCollection<Policy>, isVendorMatchingBetaEnabled = true): string[] {
          const uniqueVendorNames = new Set<string>();
          const policyIDs = getVendorFeaturePolicyIDs(allPolicies, isVendorMatchingBetaEnabled);
          for (const policyID of policyIDs) {
              const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
              const vendors = getMatchingVendors(policy);
              for (const vendor of vendors) {
                  if (vendor.name) {
                      uniqueVendorNames.add(vendor.name);
                  }
              }
          }
          return Array.from(uniqueVendorNames);
      }
      • Add CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR to CONTINUATION_DETECTION_SEARCH_FILTER_KEYS:
      --- a/src/libs/SearchAutocompleteUtils.ts
      +++ b/src/libs/SearchAutocompleteUtils.ts
      @@ -22,6 +22,7 @@ const CONTINUATION_DETECTION_SEARCH_FILTER_KEYS: SearchFilterKey[] = [
           CONST.SEARCH.SYNTAX_FILTER_KEYS.PAID_BY,
           CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPORTER,
           CONST.SEARCH.SYNTAX_FILTER_KEYS.ATTENDEE,
      +    CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR,
       ];
    2. In src/hooks/useAutocompleteSuggestions.ts:

      • Add the CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR case to useAutocompleteSuggestions:
      --- a/src/hooks/useAutocompleteSuggestions.ts
      +++ b/src/hooks/useAutocompleteSuggestions.ts
      @@ -249,6 +249,18 @@ function useAutocompleteSuggestions({
                       mapKey: CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE,
                   }));
               }
      +        case CONST.SEARCH.SYNTAX_FILTER_KEYS.VENDOR: {
      +            const vendorAutocompleteList = getAutocompleteVendors(policies, isVendorMatchingBetaEnabled);
      +            const filteredVendors = vendorAutocompleteList
      +                .filter((vendor) => vendor.toLowerCase().includes(autocompleteValue.toLowerCase()) && !alreadyAutocompletedKeys.has(vendor.toLowerCase()))
      +                .sort()
      +                .slice(0, 10);
      +
      +            return filteredVendors.map((vendorName) => ({
      +                filterKey: CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.VENDOR,
      +                text: vendorName,
      +            }));
      +        }
               case CONST.SEARCH.SYNTAX_FILTER_KEYS.ASSIGNEE:
               case CONST.SEARCH.SYNTAX_FILTER_KEYS.TO:
    3. In src/components/Search/SearchAutocompleteInput.tsx:

      • Subscribe to ONYXKEYS.COLLECTION.POLICY and provide vendorSharedValue via getAutocompleteVendors:
      --- a/src/components/Search/SearchAutocompleteInput.tsx
      +++ b/src/components/Search/SearchAutocompleteInput.tsx
      @@ -16,7 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
       import {setSearchContext} from '@libs/actions/Search';
       import scheduleOnLiveMarkdownRuntime from '@libs/scheduleOnLiveMarkdownRuntime';
      -import {getAutocompleteCategories, getAutocompleteTags, parseForLiveMarkdown} from '@libs/SearchAutocompleteUtils';
      +import {getAutocompleteCategories, getAutocompleteTags, getAutocompleteVendors, parseForLiveMarkdown} from '@libs/SearchAutocompleteUtils';
       import {expensifyLoginsSelector} from '@libs/UserUtils';
      • Pass vendorSharedValue into parseForLiveMarkdown and update filterOutRangesWithCorrectValue in SearchAutocompleteUtils.ts to validate against vendorList.get().includes(range.value) || range.value.length > 0.

    When a user types vendor: or vendor:A, autocomplete presents A B. Upon selection, SearchRouter.tsx runs sanitizeSearchValue("A B"), automatically quoting it as vendor:"A B" . The entire vendor name is highlighted as a single live markdown range, and the parsed AST filter matches vendor: "A B", properly displaying all matching expenses.

    What alternative solutions did you explore? (Optional)

    Allowing unquoted multi-word strings directly in Peggy grammar:
    Not recommended because whitespace is the canonical token delimiter across Expensify's search parser grammar. Changing alphanumeric to swallow spaces would introduce severe syntactic ambiguities with trailing operators, keywords, and subsequent filter keys. Relying on autocomplete selection with sanitizeSearchValue preserves 100% consistency with existing filters such as category:"Travel Meals", from:"John Doe", and merchant:"United Airlines".

    What automated tests can you add to prevent this from re-occurring?

    1. In tests/unit/hooks/useAutocompleteSuggestions.test.ts:
      • Add a unit test verifying that autocompleteQueryValue: 'vendor:' returns matching vendor suggestions from connected workspace policies.
      • Add a unit test verifying continuation detection for multi-word vendor names (e.g. vendor:Admin Denis), ensuring suggestions are returned even when spaces are present before quotes are applied.
    2. In tests/unit/SearchAutocompleteUtilsTest.ts:
      • Add a unit test verifying getAutocompleteVendors extracts unique vendor names across policies.
      • Add a unit test asserting parseForLiveMarkdown correctly highlights multi-word quoted vendors (vendor:"A B").

    Contributor details
    Your Expensify account email: [email protected]
    Upwork Profile Link: https://www.upwork.com/freelancers/~01f7d7991785f2ee01

  13. melvin-bot commented on Oct 29, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  14. ikevin127 commented on Oct 29, 2026

    @ikevin127
    Contributor

    Proposals review

    Only MelvinBot's proposal counts here, since this issue never had the Help Wanted label.

    Not a bug

    I tested this with a QBO workspace (company card expenses exported as Credit card) and non-reimbursable expenses with synced vendors A A and Five Stars. Everything matches MelvinBot's root cause:

    • type:expense vendor:A A: only the first A is highlighted and nothing is returned.
    • type:expense vendor:"A A": the full name is highlighted and the matching expense is returned.
    • type:expense vendor:Five Stars: only Five is highlighted and nothing is returned.
    • type:expense vendor:"Five Stars": the full name is highlighted and the matching expense is returned.

    A value without quotes ends at the first space (baseRules.peggy:221), so vendor:Five Stars searches vendor:Five plus the keyword Stars. The same is true for every free-text filter (merchant:, description:). Multi-word values need quotes.

    The issue's colonless searches (vendor A B, vendor Admin Denis) aren't vendor filters at all. They're keyword searches, so nothing is highlighted and the server decides what matches. The test steps come from Tests step 6 of #104351 (type:expense vendor:<name>), which didn't say that multi-word names need quotes.

    Recommendation

    Close this as not a bug, and update the regression test case to quote multi-word vendor names, e.g. type:expense vendor:"A B".

    MelvinBot's vendor: autocomplete idea would help users find the right syntax, but it's a new feature rather than a fix (merchant: has no autocomplete either). Up to you whether to track it separately.

    🎀👀🎀 C+ reviewed

  15. melvin-bot commented on Oct 29, 2026

    @melvin-bot

    Triggered auto assignment to @jasperhuangg, see https://stackoverflow.com/c/expensify/questions/7972 for more details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributor

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions