Skip to content

Category - Category appears as disabled when the category is A: B: #85357

Closed

Description

@nlemma

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.3.38-0
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from BrowserStack: #83200
Email or phone of affected tester (no customers): sdijnsoijsodosj@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Mac 26.2 / Chrome
App Component: Money Requests

Action Performed:

Precondition:

  • Create this category - A: B:
  1. Go to staging.new.expensify.com
  2. Go to workspace chat.
  3. Click + > Create expense > Manual.
  4. Enter amount > Next.
  5. Click Category.

Expected Result:

B: will be enabled.

Actual Result:

B: is not a category. Both A and B are disabled. A: B: is a disabled category.

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7104074_1773656728079.3.mp4

View all open jobs on GitHub

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    BugSomething is broken. Auto assigns a BugZero manager.
    on Mar 16, 2026
  2. melvin-bot commented on Mar 16, 2026

    @melvin-bot

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

  3. melvin-bot commented on Mar 16, 2026

    @melvin-bot

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

  4. github-actions commented on Mar 16, 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.
  5. OSBotify commented on Mar 16, 2026

    @OSBotify
    Contributor

    Investigation Summary

    Classification: Frontend bug
    Causing PR: #83200 - "Multi-level categories with colons are shown with hierarchical indentation" by @ijmalik (High confidence)
    Related Issues: #82068

    Recommendation: REVERT

    The causing PR (#83200) changed CONST.PARENT_CHILD_SEPARATOR from ": " (colon with space) to ":" (colon only). This breaks categories that have spaces in their names after colons, such as "A: B:".

    Why revert?
    The fix attempted to enable hierarchical category display but did not account for category names with spacing variations. Categories like "A: B:" (with trailing colon and spaces) are now incorrectly parsed:

    • Split by ":" produces ["A", " B", ""]
    • The empty string at the end causes incorrect hierarchy construction
    • Both parent and child appear as disabled

    A revert will restore the previous behavior while a proper fix can be developed that handles both hierarchical categories AND categories with spaces around colons.

    Labels: No label changes needed. This is a Frontend bug that blocks the App deploy, so DeployBlockerCash should remain.

    Detailed Analysis

    Evidence

    The bug is reproducible only on staging (version 9.3.38-0), not on production. PR #83200 is included in the current staging deploy and modified the category parsing logic. The melvin-bot comment confirms this PR was recently merged by the same author.

    Verification

    PR #83200 modified:

    • src/CONST/index.ts - Changed PARENT_CHILD_SEPARATOR from ": " to ":"
    • src/libs/CategoryOptionListUtils.ts - Removed the isOneLine parameter and changed how categories are split and displayed

    The affected code in CategoryOptionListUtils.ts line 36:

    const array = option.name.split(CONST.PARENT_CHILD_SEPARATOR);

    When a category is named "A: B:" (with spaces after colons):

    The trailing empty string and spaces cause the hierarchy construction to fail.

    Root Cause

    The PR attempted to enable hierarchical category display by treating ":" as the separator, but did not handle categories where the colon is part of the category name with spaces (e.g., "A: B:"). The loop in getCategoryOptionTree processes the empty string as a third level, causing the entire category hierarchy to be marked as disabled.

    The category name "A: B:" should be treated as a complete category name, not as a parent-child hierarchy, but the current implementation cannot distinguish between intentional hierarchy separators and colons that are part of the category name.

  6. MelvinBot commented on Mar 16, 2026

    @MelvinBot
    Contributor

    Proposal

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

    When a user creates a category with a name that ends in a colon (e.g., A: B:), the category picker displays it incorrectly: instead of showing B: as an enabled category under parent A, both A and B appear as disabled parents, and the actual category A: B: is never shown as a selectable option.

    What is the root cause of that problem?

    PR #83200 changed PARENT_CHILD_SEPARATOR from ': ' (colon-space) to ':' (just colon) in src/CONST/index.ts:6053 to fix issue #82068 (multi-level categories from integrations like QuickBooks weren't displaying hierarchically).

    This causes a problem when a category name ends with a colon. In getCategoryOptionTree (src/libs/CategoryOptionListUtils.ts:36-42):

    1. "A: B:".split(':') produces ['A', ' B', ''] — the trailing colon creates an empty string as the last element
    2. The guard if (!optionName) { continue; } at line 40 skips the empty string at index 2
    3. Only index 2 (the last element) would qualify as isChild (the selectable leaf node) per line 45: const isChild = array.length - 1 === index
    4. Since that index is skipped, the category is never added as an enabled, selectable option
    5. Indices 0 ('A') and 1 (' B') are treated as non-leaf parents, which are disabled because no standalone categories named 'A' or 'A: B' exist

    The same splitting issue exists in sortCategories at line 223, where lodashSet receives a path with an empty-string key, creating a malformed hierarchy entry.

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

    In getCategoryOptionTree in src/libs/CategoryOptionListUtils.ts, after splitting the category name on ':', filter out trailing empty strings and reattach the colon to the preceding segment. This ensures that a category name like "A: B:" splits into ['A', ' B:'] instead of ['A', ' B', ''], preserving the trailing colon as part of the leaf name.

    The same adjustment should be applied in sortCategories (line 222-231) where the split path is used with lodashGet/lodashSet to build the hierarchy object.

    Additionally, a test case for category names with trailing colons should be added to tests/unit/CategoryOptionListUtilsTest.ts to prevent regression.

    What alternative solutions did you explore? (Optional)

    Reverting PARENT_CHILD_SEPARATOR back to ': ': This would fix the trailing-colon issue but would re-introduce the original bug from #82068 where integration categories using ':' without spaces (e.g., Primary:Secondary:Tertiary) wouldn't display hierarchically.

    Changing the empty-string guard to merge all empty segments: Instead of skipping empty segments with continue, one could merge consecutive empty segments into the previous segment. This is more general but adds complexity for a narrow edge case.


    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot <your feedback> to refine this analysis, or explain why you are rejecting Melvin's proposal.

  7. ZhenjaHorbach commented on Mar 16, 2026

    @ZhenjaHorbach
    Contributor

    Oh
    I suppose it's our
    CC: @ijmalik

    But I'm not sure that is a deploy blocker
    Since it's an edge case

    And actually, in OD it also works broken 😅
    Image

  8. ijmalik commented on Mar 16, 2026

    @ijmalik
    Contributor
  9. ZhenjaHorbach commented on Mar 16, 2026

    @ZhenjaHorbach
    Contributor

    @zeh

    Should we work like this proposal?

    Let's fix higher priority regressions first
    Plus Melvin proposal is the wrong fix

    #85359
    #85364

  10. ZhenjaHorbach commented on Mar 16, 2026

    @ZhenjaHorbach
    Contributor

    Oh I suppose it's our CC: @ijmalik

    But I'm not sure that is a deploy blocker Since it's an edge case

    And actually, in OD it also works broken 😅 Image

    @Expensify/design
    Could you please clarify this issue?
    Since this test case is a bit broken in OD, too
    How should we handle it in ND?

    CC: @ijmalik

  11. shawnborton commented on Mar 16, 2026

    @shawnborton
    Contributor

    I don't feel strongly, let's get the opinion of @Expensify/product on this one too.

    Melvin's suggestion seems reasonable, where we'd just create a category of B and strip the trailing colon and space? But given that this exists in OD, I could also see an argument to just do nothing here.

  12. dannymcclain commented on Mar 16, 2026

    @dannymcclain
    Contributor

    Yeah I feel the exact same as Shawn.

  13. IuliiaHerets commented on Mar 16, 2026

    @IuliiaHerets

    Chrome / v9.3.38-1
    Passed

    Screen.Recording.2026-03-16.at.21.58.55.mp4
  14. lanitochka17 commented on May 19, 2026

    @lanitochka17

    Deploy Blocker ##91133 was identified to be related to the PR ##85448 that attempted to fix this issue.

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

Metadata

Metadata

Assignees

Labels

BugSomething is broken. Auto assigns a BugZero manager.EngineeringHourlyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions