Skip to content

[Due for payment 2026-10-28] [$175] Rules - Category that has already been selected in existing rule can be selected again #103863

Closed

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: 9.5.8-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: #99624
Email or phone of affected tester (no customers): [email protected]
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Mac 26.5 / Chrome
App Component: Workspace Settings

Action Performed:

Precondition:

  • Create a Control workspace.
  • Create Rules.
  1. Go to staging.new.expensify.com
  2. Go to workspace settings > Rules.
  3. Click Add rule.
  4. Click Set field requirements
  5. Click Describe.
  6. Enter "When category is Advertising, it requires description" and click Next.
  7. Save the rule.
  8. Repeat Step 3 to 7.
  9. Save the rule.

Expected Result:

Category that has already been selected in existing rule cannot be selected again via command.

Actual Result:

Category that has already been selected in existing rule can be selected again via command.
When trying to Save rule, it shows error "Please select at least one field".

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7285220_1791889261879.6.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Nathan Lemma
Bug ID: 7285220

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

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    BugSomething is broken. Auto assigns a BugZero manager.
    on Oct 13, 2026
  2. applause-bot commented on Oct 13, 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/c0294a878c5143dbca143dcf00d0859d336b056e54cdfb19820ecde71617be5f

  3. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    You have been assigned to this deploy blocker because you recently merged this PR: #99624

  4. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    @luacmartins @JS00001 @Krishna2323 you have been assigned to this deploy blocker because you reviewed the offending PR: #99624

  5. github-actions commented on Oct 13, 2026

    @github-actions
    Contributor

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

    1. Identify the pull request that introduced this issue and revert it.
    2. Find someone who can quickly fix the issue.
    3. Fix the issue yourself.
  6. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    @rushatgabhane you have been assigned to this deploy blocker because you authored the offending PR: #99624

  7. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    💬 A slack conversation has been started in #expensify-open-source

  8. 5 remaining items

  9. changed the title [-]Rules - Category that has already been selected in existing rule can be selected again[/-] [+][$175] Rules - Category that has already been selected in existing rule can be selected again[/+] on Oct 13, 2026
  10. melvin-bot commented on Oct 13, 2026

    @melvin-bot
  11. janiya2 commented on Oct 13, 2026

    @janiya2

    Proposal

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

    When creating a rule using the "Describe" flow (AI Concierge rule prompt) in Workspace Settings > Rules (under "Set field requirements" or "Flag for review"), if the generated rule targets a category that already has an existing rule of that type (e.g. "Advertising"), the category is still accepted and populated into the draft rule.

    When the user subsequently saves the rule, saving fails with "Please select at least one field" (in RequireFieldsRules) because no new modifications are detected against the category's existing settings. In contrast, the manual category picker correctly excludes categories that already have rules.

    What is the root cause of that problem?

    1. In src/pages/workspace/rules/DescribeRuleModal.tsx, withPolicyCategory only verifies whether the category exists in policyCategories and is enabled (candidate.enabled). It does not verify whether the category is eligible for a new rule:
      • For RequireFieldsRules: RequireFieldsRuleCategoryPageBase.tsx excludes existing rules with !categoryHasAnyRequireFieldsRule(category), but applyGeneratedRule in RequireFieldsRulePageBase.tsx writes values.category into the draft unconditionally.
      • For FlagForReviewRules: FlagForReviewRuleCategoryPageBase.tsx excludes existing rules with !hasExplicitFlagAmount(category.maxExpenseAmount), but FlagForReviewRulePageBase.tsx passes setDraftFlagForReviewRule directly to onRuleGenerated.
    2. When an already-assigned category is populated and saved, hasRequireFieldsRuleChanges evaluates to false, returning confirmErrorDoNotRequireField ("Please select at least one field").

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

    Approach (Consistent with withPolicyCategory design):
    In withPolicyCategory, missing or disabled categories are already dropped so the user must select an eligible category. We should extend this to drop categories that are not eligible for a new rule.

    1. Add an optional isCategoryEligible?: (category: PolicyCategory) => boolean prop to DescribeRuleButtonProps and DescribeRuleModalProps.

    2. Update withPolicyCategory in src/pages/workspace/rules/DescribeRuleModal.tsx:

    function withPolicyCategory(
        values: GeneratedRuleValues,
        policyCategories: OnyxEntry<PolicyCategories>,
        isCategoryEligible?: (category: PolicyCategory) => boolean,
    ): GeneratedRuleValues {
        const {category, ...rest} = values;
        if (!category) {
            return values;
        }
        const policyCategory = Object.values(policyCategories ?? {}).find(
            (candidate) => candidate.enabled && (!isCategoryEligible || isCategoryEligible(candidate)) && getDecodedCategoryName(candidate.name) === getDecodedCategoryName(category),
        );
        return policyCategory ? {...rest, category: policyCategory.name} : rest;
    }

    And pass isCategoryEligible to withPolicyCategory(generatedRuleValues ?? {}, policyCategories, isCategoryEligible).

    1. In src/pages/workspace/rules/RequireFieldsRules/RequireFieldsRulePageBase.tsx:
      Pass isCategoryEligible={(category) => !categoryHasAnyRequireFieldsRule(category)} to DescribeRuleButton.

    2. In src/pages/workspace/rules/FlagForReviewRules/FlagForReviewRulePageBase.tsx:
      Pass isCategoryEligible={(category) => !hasExplicitFlagAmount(category.maxExpenseAmount)} to DescribeRuleButton.

    When an ineligible category is described, values.category is dropped. The rule fields (e.g. require description) will be pre-filled as expected, but the Category field will remain blank, prompting the user to pick an eligible category via the category picker where existing rule categories are properly filtered out.

    What alternative solutions did you explore? (Optional)

    Alternatively, we could keep the modal open and display an inline error on NEW_RULE_PROMPT_FORM when generatedRuleState === CONST.GENERATED_RULE.STATE.RULE if the category exists but !isCategoryEligible(policyCategory) (e.g. "A rule already exists for this category"). However, dropping the ineligible category matches how withPolicyCategory already handles disabled or non-existent categories without requiring new localized prompt error strings across 11 languages.

  12. jloa-dev commented on Oct 13, 2026

    @jloa-dev

    Proposal

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

    In Workspace Settings > Rules (under "Set field requirements" or "Flag for review"), when using the AI "Describe" rule assistant (DescribeRuleModal), if a user generates a rule for a category that already has an existing rule of that type, the category is accepted into the draft. Upon attempting to save, the user is presented with a confusing validation error ("Please select at least one field") because no differences from the existing rule are found, unlike the manual category picker which prevents re-selecting already configured categories.

    What is the root cause of that problem?

    1. Unfiltered AI Category Matching in DescribeRuleModal.tsx:
      In src/pages/workspace/rules/DescribeRuleModal.tsx, withPolicyCategory only checks if the category exists and is enabled (candidate.enabled). It does not verify category availability for rule creation:
      • For RequireFieldsRules: RequireFieldsRuleCategoryPageBase.tsx filters categories with !categoryHasAnyRequireFieldsRule(category), but RequireFieldsRulePageBase.tsx:applyGeneratedRule writes the category into draftRequireFieldsRule without checking if a rule already exists.
      • For FlagForReviewRules: FlagForReviewRuleCategoryPageBase.tsx filters categories with !hasExplicitFlagAmount(category.maxExpenseAmount), but FlagForReviewRulePageBase.tsx passes setDraftFlagForReviewRule directly.
    2. Generic Validation Failure on Save:
      When an already assigned category is saved, hasRequireFieldsRuleChanges returns false, causing RequireFieldsRulesUtils.ts:534 to fall back to confirmErrorDoNotRequireField ("Please select at least one field") instead of highlighting that the category is already assigned.

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

    1. Category Eligibility Validator in DescribeRuleModal.tsx:
      Add an isCategoryEligible predicate to DescribeRuleModalProps and withPolicyCategory:

      function withPolicyCategory(
          values: GeneratedRuleValues,
          policyCategories: OnyxEntry<PolicyCategories>,
          isCategoryEligible?: (category: PolicyCategory) => boolean,
      ): {values: GeneratedRuleValues; isCategoryIneligible?: boolean} {
          const {category, ...rest} = values;
          if (!category) {
              return {values};
          }
          const policyCategory = Object.values(policyCategories ?? {}).find(
              (candidate) => candidate.enabled && getDecodedCategoryName(candidate.name) === getDecodedCategoryName(category),
          );
          if (!policyCategory) {
              return {values: rest};
          }
          if (isCategoryEligible && !isCategoryEligible(policyCategory)) {
              return {values: rest, isCategoryIneligible: true};
          }
          return {values: {...rest, category: policyCategory.name}};
      }
    2. Pass Filter Predicate from Rule Page Bases:

      • In src/pages/workspace/rules/RequireFieldsRules/RequireFieldsRulePageBase.tsx:
        Pass isCategoryEligible={(category) => !categoryHasAnyRequireFieldsRule(category)} to DescribeRuleButton.
      • In src/pages/workspace/rules/FlagForReviewRules/FlagForReviewRulePageBase.tsx:
        Pass isCategoryEligible={(category) => !hasExplicitFlagAmount(category.maxExpenseAmount)} to DescribeRuleButton.
    3. Explicit Feedback in DescribeRuleModal:
      If isCategoryIneligible is detected, display an inline notice or toast notifying the user that a rule for this category already exists and leaving the category selector open for an eligible category, preventing any cryptic save failure.

    What alternative solutions did you explore? (Optional)

    • Silently omitting the category without notifying the user: Frustrating for users who explicitly asked for that category in their AI prompt. Giving explicit feedback or highlighting the Category field is significantly better UX.

    What specific scenarios should we cover in automated tests?

    In tests/unit/DescribeRuleModalTest.tsx and tests/unit/RequireFieldsRulesUtilsTest.ts:

    1. Verify that describing a rule for a category with an existing require-fields rule rejects the category assignment and directs the user to select an available category.
    2. Verify that describing a rule for an available category successfully populates all rule fields.
  13. camesenin commented on Oct 13, 2026

    @camesenin

    Proposal

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

    In Workspace Settings > Rules (under both "Set field requirements" and "Flag for review"), when creating a rule using the AI "Describe" modal (DescribeRuleModal), a category that already has an active rule of that type can be accepted and applied into the draft rule. When the user attempts to save, it either allows duplicate category rules or fails with a confusing validation error ("Please select at least one field"), unlike the manual category picker which strictly disables/excludes already assigned categories.

    What is the root cause of that problem?

    1. Missing Eligibility Check in DescribeRuleModal.tsx:
      In src/pages/workspace/rules/DescribeRuleModal.tsx, helper withPolicyCategory matches the AI-parsed category against policyCategories:

      const policyCategory = Object.values(policyCategories ?? {}).find(
          (candidate) => candidate.enabled && getDecodedCategoryName(candidate.name) === getDecodedCategoryName(category),
      );

      It checks candidate.enabled, but completely ignores whether the category already has an existing rule of the relevant type:

      • For RequireFieldsRules: RequireFieldsRuleCategoryPageBase.tsx uses !categoryHasAnyRequireFieldsRule(category) to filter options, but DescribeRuleModal bypasses this filter entirely.
      • For FlagForReviewRules: FlagForReviewRuleCategoryPageBase.tsx uses !hasExplicitFlagAmount(category.maxExpenseAmount), but DescribeRuleModal also bypasses it.
    2. Silently Passing Ineligible Categories to Draft State:
      Because withPolicyCategory does not validate rule eligibility, applyGeneratedRule in RequireFieldsRulePageBase.tsx and setDraftFlagForReviewRule in FlagForReviewRulePageBase.tsx inject the already assigned category into the draft rule.

    3. Cryptic Save Failure:
      When saving an already configured category rule without other field modifications, hasRequireFieldsRuleChanges evaluates to false, causing RequireFieldsRulesUtils.ts:534 to fall back to the generic error confirmErrorDoNotRequireField ("Please select at least one field"), leaving the user confused about why the rule cannot be saved.

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

    1. Add isCategoryEligible Predicate to DescribeRuleModal:
      In src/pages/workspace/rules/DescribeRuleModal.tsx, update withPolicyCategory to accept an isCategoryEligible?: (category: PolicyCategory) => boolean callback:

      function withPolicyCategory(
          values: GeneratedRuleValues,
          policyCategories: OnyxEntry<PolicyCategories>,
          isCategoryEligible?: (category: PolicyCategory) => boolean,
      ): {values: GeneratedRuleValues; isCategoryIneligible?: boolean} {
          const {category, ...rest} = values;
          if (!category) {
              return {values};
          }
          const policyCategory = Object.values(policyCategories ?? {}).find(
              (candidate) => candidate.enabled && getDecodedCategoryName(candidate.name) === getDecodedCategoryName(category),
          );
          if (!policyCategory) {
              return {values: rest};
          }
          if (isCategoryEligible && !isCategoryEligible(policyCategory)) {
              return {values: rest, isCategoryIneligible: true};
          }
          return {values: {...rest, category: policyCategory.name}};
      }
    2. Supply Contextual Filter from Rule Page Bases:

      • In src/pages/workspace/rules/RequireFieldsRules/RequireFieldsRulePageBase.tsx:
        Pass isCategoryEligible={(category) => !categoryHasAnyRequireFieldsRule(category)} to DescribeRuleButton and DescribeRuleModal.
      • In src/pages/workspace/rules/FlagForReviewRules/FlagForReviewRulePageBase.tsx:
        Pass isCategoryEligible={(category) => !hasExplicitFlagAmount(category.maxExpenseAmount)} to DescribeRuleButton and DescribeRuleModal.
    3. User Feedback & Seamless Fallback:
      When isCategoryIneligible is true, omit the category from the applied draft rule values and set focus / show a notification informing the user that a rule already exists for this category, opening the standard Category picker where only available categories are listed.

    4. Automated Unit Tests:
      In tests/unit/DescribeRuleModalTest.tsx:

      • Verify that describing a rule for an unassigned category populates the category field.
      • Verify that describing a rule for an already assigned category strips the category and flags isCategoryIneligible.

    What alternative solutions did you explore? (Optional)

    • Displaying an inline form error within DescribeRuleModal: This requires adding new localized error strings across all 11 supported languages. Stripping the ineligible category and falling back to the existing category selection page achieves the same UX consistency with zero localization overhead.

    Contributor details
    Your Expensify account email: [email protected]
    Upwork Profile link: https://www.upwork.com/freelancers/~0170d6b02e9440e604

  14. melvin-bot commented on Oct 13, 2026

    @melvin-bot

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

  15. kavimuru commented on Oct 13, 2026

    @kavimuru

    Describe button is not showing with "describerule" disabled.

    2026-10-13.20-38-55.mp4
  16. added
    ReviewingHas a PR in review
    and removed
    ReviewingHas a PR in review
    on Oct 15, 2026
  17. changed the title [-][$175] Rules - Category that has already been selected in existing rule can be selected again[/-] [+][Due for payment 2026-10-28] [$175] Rules - Category that has already been selected in existing rule can be selected again[/+] on Oct 21, 2026
  18. melvin-bot commented on Oct 21, 2026

    @melvin-bot

    @Krishna2323

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.5.15-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2026-10-28. 🎊

    The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the Contributor+ Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]. If no checklist is needed for this issue, you can click: [no checklist button]

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