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-10-13] [$175] Spend - Categories are not visible on fresh sign in in a expense #98838
Version Number: 9.4.54-4 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack: Email or phone of affected tester (no customers): Logs:https://stackoverflow.com/c/expensify/questions/4856 Expensify/Expensify Issue URL: Issue reported by:@mukhrr Slack conversation (hyperlinked to channel name): #expensify_open_source
Action Performed:
Prerequisites: high traffic account
1.Go to https://staging.new.expensify.com/
2. Create a workspace and expense in it with high traffic account
3. Fresh sign in to high-traffic account
4. Go to expenses page
5. Try to change category of created expense in step 1
Expected Result:
All categories are visible on popover
Actual Result:
Only selected categpry is visible
Workaround:
Unknown
Platforms:
Select the officially supported platforms where the issue was reproduced:
Please re-state the problem that we are trying to solve in this issue.
On a fresh sign-in with a high-traffic account, opening the inline category picker on the Spend > Expenses page shows only the already-selected category instead of the full list of workspace categories.
What is the root cause of that problem?
High-traffic accounts load Onyx data in a delayed/chunked way, so on a fresh sign-in the policyCategories collection for the workspace is empty (or contains only the category already on the expense). The inline popover used on the expenses page never fetches the missing categories and has no loading gate.
Because the expense already has a category, CategoryPicker synthesizes a one-item selected list, and getCategoryListSections hits the "no enabled categories but a selection exists" branch at CategoryOptionListUtils.ts:131-141, which renders a single "Selected" section containing only that one category — exactly the reported symptom.
Why the full-page RHP category step is NOT affected (and its own latent guard bug)
The full-page step DynamicIOURequestStepCategory.tsx:149-166does call getPolicyCategories on mount and shows a spinner while policyCategories === undefined, so it recovers. But its guard if ((!!policy && !!policyCategories) || !policyID) return; also skips the fetch when policyCategories is a non-empty but partial object, so it would still under-fetch if the payload contained only the selected category. The primary bug for this issue is the inline popover path, which has no fetch at all.
getPolicyCategories is defined at Category.ts:298-309. The App never fetches categories during OpenApp/ReconnectApp, so nothing else backfills them for this popover.
What changes do you think we should make in order to solve the problem?
Give the inline popover path the same load behavior the full-page step has:
In CategoryPickerModal (or CategoryCell), call getPolicyCategories(policyID) when the popover opens and the policy's categories are missing/empty, so the full list is fetched on demand.
Show a loading state in the popover while categories are being fetched instead of falling through to the "Selected-only" section — treat empty/loading distinctly from "genuinely no categories."
While there, consider fixing the full-page step's guard so it re-fetches when only a partial policyCategories object is present (change the !!policyCategories truthiness check to also fetch when the collection has no enabled options).
What alternative solutions did you explore?
Centralizing the fetch in CategoryPicker itself (so every consumer benefits) is an option, but scoping it to the popover entry point keeps the change minimal and mirrors the existing working pattern.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/98838) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Here's a patch you can apply to test the proposed fix. It adds the missing category fetch to CategoryPicker (used by both the inline expenses-page popover and other consumers), gated to fire when no enabled categories are loaded — which covers both the empty and "only the selected category" states seen on a fresh high-traffic sign-in.
Save the block below as categories-fresh-signin.patch at the repo root, then apply with git apply categories-fresh-signin.patch:
diff --git a/src/components/CategoryPicker/index.tsx b/src/components/CategoryPicker/index.tsx
index dc6be19..a6ac7db 100644
--- a/src/components/CategoryPicker/index.tsx+++ b/src/components/CategoryPicker/index.tsx@@ -10,6 +10,7 @@ import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
+import {getPolicyCategories} from '@libs/actions/Policy/Category';
import {getCategoryListSections} from '@libs/CategoryOptionListUtils';
import type {Category} from '@libs/CategoryOptionListUtils';
import {getEnabledCategoriesCount} from '@libs/CategoryUtils';
@@ -21,7 +22,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import React from 'react';+import React, {useEffect} from 'react';
// eslint-disable-next-line no-restricted-imports -- Need original useOnyx to avoid reading partial Search snapshot policy data (GL code flags are trimmed from the snapshot).
import {useOnyx as originalUseOnyx} from 'react-native-onyx';
@@ -105,6 +106,17 @@ function CategoryPicker({selectedCategory, policyID, onSubmit, shouldShowNoneOpt
const categoriesCount = getEnabledCategoriesCount(categories);
const selectedOptionKey = categoryData.find((category) => category.searchText === selectedCategory)?.keyForList;
+ // On a fresh sign-in (e.g. high-traffic accounts load Onyx data lazily), the policy's categories may be+ // missing or only contain the already-selected category. Fetch the full list on mount so the picker isn't+ // stuck showing just the selected category. Runs once per policyID; skipped offline and when categories exist.+ useEffect(() => {+ if (isOffline || !policyID || categoriesCount > 0) {+ return;+ }+ getPolicyCategories(policyID);+ // eslint-disable-next-line react-hooks/exhaustive-deps+ }, [policyID, isOffline]);+
const textInputOptions = {
value: searchValue,
label: translate('common.search'),
Notes on the approach
The inline popover path (CategoryCell → CategoryPickerModal → CategoryPicker) previously never fetched categories and had no loading gate, so on a fresh high-traffic sign-in it fell through to the "Selected"-only section in CategoryOptionListUtils.ts:131-141.
Putting the fetch in CategoryPicker fixes every consumer at once and, by gating on categoriesCount > 0 (enabled-categories count) rather than !!policyCategories, it also covers the partial-collection case where only the selected category is present — which the truthiness guard in DynamicIOURequestStepCategory.tsx:149-155 would miss.
getPolicyCategories fires the GetPolicyCategories read command (Category.ts:298-309); the effect runs once per policyID and skips offline, so it won't loop.
This is a test patch, not a merged change — it hasn't gone through CI checks (lint/typecheck/tests). If it looks good, reply @MelvinBot implement [this](https://github.com/Expensify/App/issues/98838) and I'll open a proper draft PR with the checks run.
@mukhrr Thx, however, according to new rule, I think will work with Melvin first to find the optimal solution, if it doesn't then I will open for external
@MelvinBot The root-cause direction looks plausible, but the patch currently proves only the empty-collection case.
selectedOptions is synthesized independently from policyCategories, so the UI can display one selected category while categoriesCount === 0. In that state, this patch correctly triggers the fetch.
However, if the high-traffic payload actually contains a non-empty partial policyCategories object—including only the selected category—then categoriesCount > 0 prevents the fetch. The client also cannot infer collection completeness merely from the enabled-category count, because a workspace may legitimately have only one enabled category.
Before finalizing the solution, I suggest inspecting the reproduced Onyx state and confirming:
whether policyCategories is undefined, empty, or partially populated;
whether there is an existing loading/pending indicator for GetPolicyCategories;
whether opening multiple picker consumers could cause redundant requests;
whether the picker should render a loading state until the read completes.
The proposed loading behavior is also not included in this test patch. If testing confirms that policyCategories is empty, this is a reasonable minimal fix, but the proposal should not claim that it handles a non-empty partial collection without a reliable completeness signal.
📣 @BackendArchitectX! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
Please follow these steps:
Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
changed the title [-][$175] Spend - Categories are not visible on fresh sign in in a expense[/-][+][Due for payment 2026-10-13] [$175] Spend - Categories are not visible on fresh sign in in a expense[/+]on Oct 6, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.5.3-4 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-13. 🎊
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]
34. @mukhrr let's always wait to all PRs have been merged for a week then discuss. We'll have a better idea then. thx. I'll try to remember... please try to remind me too, inc. @thelullabyy (I want to build a process and rubric for this)
@mukhrr we don't have a process for compensating on issues where Help Wanted isn't added. The reason is cuz, if we start paying people for non-Help Wanted issues, they'll start getting flooded with AI-assisted proposals, similar to how we get a dozen proposals within a few minutes when Help Wanted is added now. I have been discussing this internally, and we appreciate your help, but we're unable to currently pay.
Activity
MelvinBot commentedon Aug 18, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On a fresh sign-in with a high-traffic account, opening the inline category picker on the Spend > Expenses page shows only the already-selected category instead of the full list of workspace categories.
What is the root cause of that problem?
High-traffic accounts load Onyx data in a delayed/chunked way, so on a fresh sign-in the
policyCategoriescollection for the workspace is empty (or contains only the category already on the expense). The inline popover used on the expenses page never fetches the missing categories and has no loading gate.The repro path is
MoneyRequestReportView→CategoryCell.tsx:64-73→CategoryPickerModal.tsx→CategoryPicker/index.tsx. None of these ever callgetPolicyCategories/OpenPolicyCategoriesPage— the picker just reads whatever is in Onyx.Because the expense already has a category,
CategoryPickersynthesizes a one-item selected list, andgetCategoryListSectionshits the "no enabled categories but a selection exists" branch atCategoryOptionListUtils.ts:131-141, which renders a single "Selected" section containing only that one category — exactly the reported symptom.Why the full-page RHP category step is NOT affected (and its own latent guard bug)
The full-page step
DynamicIOURequestStepCategory.tsx:149-166does callgetPolicyCategorieson mount and shows a spinner whilepolicyCategories === undefined, so it recovers. But its guardif ((!!policy && !!policyCategories) || !policyID) return;also skips the fetch whenpolicyCategoriesis a non-empty but partial object, so it would still under-fetch if the payload contained only the selected category. The primary bug for this issue is the inline popover path, which has no fetch at all.getPolicyCategoriesis defined atCategory.ts:298-309. The App never fetches categories duringOpenApp/ReconnectApp, so nothing else backfills them for this popover.What changes do you think we should make in order to solve the problem?
Give the inline popover path the same load behavior the full-page step has:
CategoryPickerModal(orCategoryCell), callgetPolicyCategories(policyID)when the popover opens and the policy's categories are missing/empty, so the full list is fetched on demand.policyCategoriesobject is present (change the!!policyCategoriestruthiness check to also fetch when the collection has no enabled options).What alternative solutions did you explore?
Centralizing the fetch in
CategoryPickeritself (so every consumer benefits) is an option, but scoping it to the popover entry point keeps the change minimal and mirrors the existing working pattern.Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/98838)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
melvin-bot commentedon Aug 18, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @thelullabyy (
External)melvin-bot commentedon Aug 18, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022089588752379915124
thelullabyy commentedon Aug 18, 2026
@MelvinBot Please give me patch file to test your proposal
MelvinBot commentedon Aug 18, 2026
Here's a patch you can apply to test the proposed fix. It adds the missing category fetch to
CategoryPicker(used by both the inline expenses-page popover and other consumers), gated to fire when no enabled categories are loaded — which covers both the empty and "only the selected category" states seen on a fresh high-traffic sign-in.Save the block below as
categories-fresh-signin.patchat the repo root, then apply withgit apply categories-fresh-signin.patch:Notes on the approach
CategoryCell→CategoryPickerModal→CategoryPicker) previously never fetched categories and had no loading gate, so on a fresh high-traffic sign-in it fell through to the "Selected"-only section inCategoryOptionListUtils.ts:131-141.CategoryPickerfixes every consumer at once and, by gating oncategoriesCount > 0(enabled-categories count) rather than!!policyCategories, it also covers the partial-collection case where only the selected category is present — which the truthiness guard inDynamicIOURequestStepCategory.tsx:149-155would miss.getPolicyCategoriesfires theGetPolicyCategoriesread command (Category.ts:298-309); the effect runs once perpolicyIDand skips offline, so it won't loop.This is a test patch, not a merged change — it hasn't gone through CI checks (lint/typecheck/tests). If it looks good, reply
@MelvinBot implement [this](https://github.com/Expensify/App/issues/98838)and I'll open a proper draft PR with the checks run.mukhrr commentedon Aug 18, 2026
@thelullabyy I was issue reporter and I don't think Melvin is good
thelullabyy commentedon Aug 18, 2026
@mukhrr Thx, however, according to new rule, I think will work with Melvin first to find the optimal solution, if it doesn't then I will open for external
BackendArchitectX commentedon Aug 18, 2026
@MelvinBot The root-cause direction looks plausible, but the patch currently proves only the empty-collection case.
selectedOptionsis synthesized independently frompolicyCategories, so the UI can display one selected category whilecategoriesCount === 0. In that state, this patch correctly triggers the fetch.However, if the high-traffic payload actually contains a non-empty partial
policyCategoriesobject—including only the selected category—thencategoriesCount > 0prevents the fetch. The client also cannot infer collection completeness merely from the enabled-category count, because a workspace may legitimately have only one enabled category.Before finalizing the solution, I suggest inspecting the reproduced Onyx state and confirming:
policyCategoriesisundefined, empty, or partially populated;GetPolicyCategories;The proposed loading behavior is also not included in this test patch. If testing confirms that
policyCategoriesis empty, this is a reasonable minimal fix, but the proposal should not claim that it handles a non-empty partial collection without a reliable completeness signal.melvin-bot commentedon Aug 18, 2026
📣 @BackendArchitectX! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
Please follow these steps:
Format:
BackendArchitectX commentedon Aug 18, 2026
Contributor details
Your Expensify account email: pranayp.kadu@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~01d916f91d80c7d23a
32 remaining items
[-][$175] Spend - Categories are not visible on fresh sign in in a expense[/-][+][Due for payment 2026-10-13] [$175] Spend - Categories are not visible on fresh sign in in a expense[/+]melvin-bot commentedon Oct 6, 2026
@thelullabyy
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.5.3-4 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-13. 🎊
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]
mukhrr commentedon Oct 9, 2026
@mallenexpensify it's 3 days since this got to production.
melvin-bot commentedon Oct 12, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Oct 12, 2026
Payment Summary
Resolving PRs:
Fetch policy categories and tags on demand in shared pickers #99125
Reviewer: @thelullabyy owed $175 via NewDot
BugZero Checklist (@mallenexpensify)
mallenexpensify commentedon Oct 15, 2026
Payment Summary
Contributor+: @thelullabyy due $250 via NewDot
@mukhrr we don't have a process for compensating on issues where
Help Wantedisn't added. The reason is cuz, if we start paying people for non-Help Wantedissues, they'll start getting flooded with AI-assisted proposals, similar to how we get a dozen proposals within a few minutes whenHelp Wantedis added now. I have been discussing this internally, and we appreciate your help, but we're unable to currently pay.