You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-05-28] Category - Parent category is disabled when selected #85359
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 both categories - Car and Car: Tesla
Go to staging.new.expensify.com
Go to workspace chat.
Create an expense with Tesla subcategory.
Open the expense.
Click Category.
On the search field, type Car.
Expected Result:
Car will be enabled because Car is a valid category (there are Car and Car: Tesla categories).
Actual Result:
Car is disabled when Car is a valid category. User will have to select a different category in order to select Car category.
👋 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:
Identify the pull request that introduced this issue and revert it.
Classification: Frontend bug Causing PR: #83200 - "Multi-level categories with colons are shown with hierarchical indent..." by @ijmalik (High confidence) Related Issues: #82068
Recommendation: REVERT
PR #83200 introduced a regression in category selection logic that causes valid parent categories to be incorrectly disabled.
Labels: Keeping DeployBlockerCash label as this is a Frontend bug requiring an App PR fix
📋 Detailed Analysis
Evidence
PR #83200 was merged on 2026-03-13 to add hierarchical indentation for multi-level categories. The bug appeared in version 9.3.38-0, which includes this PR. The issue is reproducible on staging but not production, confirming the bug was introduced in this deployment.
Verification
The bug affects category selection in src/libs/CategoryOptionListUtils.ts, specifically the getCategoryOptionTree function modified by PR #83200.
Key changes in the PR:
Changed PARENT_CHILD_SEPARATOR from ": " (colon with space) to ":" (colon only)
Removed the isOneLine parameter, forcing all category displays to use hierarchical mode
Modified parent category disabled state logic
Root Cause
When categories like "Car" and "Car: Tesla" exist (note the space after colon in actual category names):
The separator change from ": " to ":" causes "Car: Tesla" to be split into ["Car", " Tesla"] instead of ["Car", "Tesla"]
During processing, a parent entry for "Car" is created from the "Car: Tesla" category
Line 48 in CategoryOptionListUtils.ts calculates: isParentOptionDisabled = !selectedParentOption || !selectedParentOption.enabled || ...
Since "Car" as a parent is not in selectedOptions, it gets disabled even though "Car" is a valid standalone enabled category
This causes the reported bug where users cannot select the "Car" category after typing "Car" in the search field
The issue occurs because the function treats any category that is a prefix of another category as a "parent" and applies different disabled logic to it, even when that category exists as a valid standalone option.
Please re-state the problem that we are trying to solve in this issue.
When a workspace has both a standalone category (e.g. "Car") and a subcategory that shares the parent name (e.g. "Car: Tesla"), searching for "Car" in the category picker while editing an expense that already uses the subcategory causes the standalone "Car" to appear disabled/grayed out. The user cannot select "Car" even though it is a valid, enabled category.
What is the root cause of that problem?
PR #83200 removed the isOneLine parameter from getCategoryOptionTree. Previously, search results used isOneLine = true, which rendered each category as a flat single-line entry without creating synthetic parent nodes. With that removed, search results now go through hierarchical tree building.
The bug is a first-write-wins collision in the option Map. Here is the chain:
When editing an expense with category "Car: Tesla", the category picker calls getCategoryListSections with selectedOptions = [{name: "Car: Tesla"}].
At CategoryOptionListUtils.ts:120, the search array is built as [...selectedOptionsWithDisabledState, ...enabledCategories], which puts "Car: Tesla" before standalone "Car".
At line 127, getCategoryOptionTree(searchCategories) is called without the selectedOptions parameter (it defaults to []).
Processing "Car: Tesla" first: it splits by ':' into ["Car", " Tesla"]. At index 0, isChild = false, and selectedParentOption is undefined (because selectedOptions is empty). So isParentOptionDisabled = true. The Map entry for key "Car" is created with isDisabled: true (line 61).
When standalone "Car" is processed next, optionCollection.has("Car") returns true (line 50), so it is skipped. The disabled parent placeholder wins.
The core issue is at line 47-48: isParentOptionDisabled only checks the selectedOptions array to determine if a parent is a real category. Since selectedOptions is empty in the search path, all synthetic parent nodes are marked disabled — even when a real, enabled category with that name exists in the options array being processed.
What changes do you think we should make in order to solve the problem?
In getCategoryOptionTree at src/libs/CategoryOptionListUtils.ts:47-48, the isParentOptionDisabled calculation should also check the options array (the actual categories being rendered), not just selectedOptions. A parent node should be enabled when a real, enabled category with the same name exists in either options or selectedOptions.
Replace the current parent disability check with one that looks up the parent name in both options and selectedOptions. If a matching enabled category is found in either source, the parent should not be disabled. This fixes the search path (where selectedOptions is empty but the real category exists in options) while preserving existing behavior for the "All" section (where selected categories are filtered out of options but exist in selectedOptions).
What alternative solutions did you explore? (Optional)
Fix the Map collision logic — instead of continue when a key already exists, overwrite if the new entry is a leaf (real category) and the existing entry is a disabled parent placeholder. This is more invasive and changes the general deduplication behavior.
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.
This behavior is not a regression introduced by PR #85448. I’ve tested the same scenario in OldDot/Classic and confirmed that a colon‑only category : cannot be selected there either – it appears as an empty line in the dropdown and clicking it does nothing (the category field remains empty).
Our PR’s behavior (the category not appearing in the list) is visually different but functionally identical to OldDot: the category : is not selectable.
For a detailed breakdown, please see my comment in the PR: #85448 (comment)
If the team decides that : should be selectable (unlike OldDot), that would be an enhancement request – but it is not a regression caused by this PR. I recommend closing this issue as “not a regression” (consistent with legacy behavior). Please let me know if you’d still like me to change the PR.
changed the title [-]Category - Parent category is disabled when selected[/-][+][Due for payment 2026-05-28] Category - Parent category is disabled when selected[/+]on May 21, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.78-1 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
@yuwenmemon The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero 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]
Activity
melvin-bot commentedon Mar 16, 2026
You have been assigned to this deploy blocker because you recently merged this PR: #83200
melvin-bot commentedon Mar 16, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Mar 16, 2026
👋 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:
OSBotify commentedon Mar 16, 2026
🔍 Investigation Summary
Classification: Frontend bug
Causing PR: #83200 - "Multi-level categories with colons are shown with hierarchical indent..." by @ijmalik (High confidence)
Related Issues: #82068
Recommendation: REVERT
PR #83200 introduced a regression in category selection logic that causes valid parent categories to be incorrectly disabled.
Labels: Keeping
DeployBlockerCashlabel as this is a Frontend bug requiring an App PR fix📋 Detailed Analysis
Evidence
PR #83200 was merged on 2026-03-13 to add hierarchical indentation for multi-level categories. The bug appeared in version 9.3.38-0, which includes this PR. The issue is reproducible on staging but not production, confirming the bug was introduced in this deployment.
Verification
The bug affects category selection in
src/libs/CategoryOptionListUtils.ts, specifically thegetCategoryOptionTreefunction modified by PR #83200.Key changes in the PR:
PARENT_CHILD_SEPARATORfrom": "(colon with space) to":"(colon only)isOneLineparameter, forcing all category displays to use hierarchical modeRoot Cause
When categories like "Car" and "Car: Tesla" exist (note the space after colon in actual category names):
": "to":"causes "Car: Tesla" to be split into["Car", " Tesla"]instead of["Car", "Tesla"]isParentOptionDisabled = !selectedParentOption || !selectedParentOption.enabled || ...The issue occurs because the function treats any category that is a prefix of another category as a "parent" and applies different disabled logic to it, even when that category exists as a valid standalone option.
MelvinBot commentedon Mar 16, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When a workspace has both a standalone category (e.g. "Car") and a subcategory that shares the parent name (e.g. "Car: Tesla"), searching for "Car" in the category picker while editing an expense that already uses the subcategory causes the standalone "Car" to appear disabled/grayed out. The user cannot select "Car" even though it is a valid, enabled category.
What is the root cause of that problem?
PR #83200 removed the
isOneLineparameter fromgetCategoryOptionTree. Previously, search results usedisOneLine = true, which rendered each category as a flat single-line entry without creating synthetic parent nodes. With that removed, search results now go through hierarchical tree building.The bug is a first-write-wins collision in the option Map. Here is the chain:
getCategoryListSectionswithselectedOptions = [{name: "Car: Tesla"}].CategoryOptionListUtils.ts:120, the search array is built as[...selectedOptionsWithDisabledState, ...enabledCategories], which puts "Car: Tesla" before standalone "Car".getCategoryOptionTree(searchCategories)is called without theselectedOptionsparameter (it defaults to[]).':'into["Car", " Tesla"]. At index 0,isChild = false, andselectedParentOptionisundefined(becauseselectedOptionsis empty). SoisParentOptionDisabled = true. The Map entry for key"Car"is created withisDisabled: true(line 61).optionCollection.has("Car")returnstrue(line 50), so it is skipped. The disabled parent placeholder wins.The core issue is at line 47-48:
isParentOptionDisabledonly checks theselectedOptionsarray to determine if a parent is a real category. SinceselectedOptionsis empty in the search path, all synthetic parent nodes are marked disabled — even when a real, enabled category with that name exists in theoptionsarray being processed.What changes do you think we should make in order to solve the problem?
In
getCategoryOptionTreeatsrc/libs/CategoryOptionListUtils.ts:47-48, theisParentOptionDisabledcalculation should also check theoptionsarray (the actual categories being rendered), not justselectedOptions. A parent node should be enabled when a real, enabled category with the same name exists in eitheroptionsorselectedOptions.Replace the current parent disability check with one that looks up the parent name in both
optionsandselectedOptions. If a matching enabled category is found in either source, the parent should not be disabled. This fixes the search path (whereselectedOptionsis empty but the real category exists inoptions) while preserving existing behavior for the "All" section (where selected categories are filtered out ofoptionsbut exist inselectedOptions).What alternative solutions did you explore? (Optional)
continuewhen a key already exists, overwrite if the new entry is a leaf (real category) and the existing entry is a disabled parent placeholder. This is more invasive and changes the general deduplication behavior.isOneLinefor search results only — this would effectively revert the search-specific change from PR Multi-level categories with colons are shown with hierarchical indent… #83200. This avoids the bug but loses the hierarchical display in search results that the PR intended to add.Next Steps for Contributor+ team: Reply with
@MelvinBot implement thisto create a draft PR,@MelvinBot <your feedback>to refine this analysis, or explain why you are rejecting Melvin's proposal.ZhenjaHorbach commentedon Mar 16, 2026
@ijmalik
Could you check this, please?
melvin-bot commentedon Mar 16, 2026
If you are the assigned CME please investigate whether the linked PR caused a regression and leave a comment with the results.
If a regression has occurred and you are the assigned CM follow the instructions here.
If this regression could have been avoided please consider also proposing a recommendation to the PR checklist so that we can avoid it in the future.
ijmalik commentedon Mar 16, 2026
ZhenjaHorbach commentedon Mar 16, 2026
@ijmalik
Any updates?
5 remaining items
ZhenjaHorbach commentedon Mar 16, 2026
It's already merged
ijmalik commentedon Mar 16, 2026
@ZhenjaHorbach Kindly check the PR. #85448
ijmalik commentedon Mar 16, 2026
@ZhenjaHorbach Kindly check the PR. #85448
IuliiaHerets commentedon Mar 16, 2026
Windows 10 / Chrome
Not Repro
v9.3.38-1
bandicam.2026-03-16.15-06-16-767.mp4
lanitochka17 commentedon May 19, 2026
Deploy Blocker ##91133 was identified to be related to the PR ##85448 that attempted to fix this issue.
melvin-bot commentedon May 20, 2026
If you are the assigned CME please investigate whether the linked PR caused a regression and leave a comment with the results.
If a regression has occurred and you are the assigned CM follow the instructions here.
If this regression could have been avoided please consider also proposing a recommendation to the PR checklist so that we can avoid it in the future.
ijmalik commentedon May 20, 2026
This behavior is not a regression introduced by PR #85448. I’ve tested the same scenario in OldDot/Classic and confirmed that a colon‑only category : cannot be selected there either – it appears as an empty line in the dropdown and clicking it does nothing (the category field remains empty).
Our PR’s behavior (the category not appearing in the list) is visually different but functionally identical to OldDot: the category : is not selectable.
For a detailed breakdown, please see my comment in the PR: #85448 (comment)
If the team decides that : should be selectable (unlike OldDot), that would be an enhancement request – but it is not a regression caused by this PR. I recommend closing this issue as “not a regression” (consistent with legacy behavior). Please let me know if you’d still like me to change the PR.
Please see OldDot Demo Video
[-]Category - Parent category is disabled when selected[/-][+][Due for payment 2026-05-28] Category - Parent category is disabled when selected[/+]melvin-bot commentedon May 21, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.78-1 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-05-28. 🎊
melvin-bot commentedon May 21, 2026
@yuwenmemon The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero 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]