Skip to content

[Due for payment 2026-10-13] [$175] Spend - Categories are not visible on fresh sign in in a expense #98838

Closed

Description

@izarutskaya

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


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:

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

Screenshots/Videos

Recording.3940.mp4

View all open jobs on GitHub

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

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Aug 18, 2026
  2. MelvinBot commented on Aug 18, 2026

    @MelvinBot
    Contributor

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

    The repro path is MoneyRequestReportView → CategoryCell.tsx:64-73 → CategoryPickerModal.tsx → CategoryPicker/index.tsx. None of these ever call getPolicyCategories / OpenPolicyCategoriesPage — the picker just reads whatever is in Onyx.

    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-166 does 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:

    1. 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.
    2. 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."
    3. 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.

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Aug 18, 2026
  4. melvin-bot commented on Aug 18, 2026

    @melvin-bot

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

  5. melvin-bot commented on Aug 18, 2026

    @melvin-bot
  6. thelullabyy commented on Aug 18, 2026

    @thelullabyy
    Contributor

    @MelvinBot Please give me patch file to test your proposal

  7. MelvinBot commented on Aug 18, 2026

    @MelvinBot
    Contributor

    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.

  8. mukhrr commented on Aug 18, 2026

    @mukhrr
    Contributor

    @thelullabyy I was issue reporter and I don't think Melvin is good

  9. thelullabyy commented on Aug 18, 2026

    @thelullabyy
    Contributor

    @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

  10. BackendArchitectX commented on Aug 18, 2026

    @BackendArchitectX

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

    1. whether policyCategories is undefined, empty, or partially populated;
    2. whether there is an existing loading/pending indicator for GetPolicyCategories;
    3. whether opening multiple picker consumers could cause redundant requests;
    4. 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.

  11. melvin-bot commented on Aug 18, 2026

    @melvin-bot

    📣 @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:

    1. Make sure you've read and understood the contributing guidelines.
    2. 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.
    3. 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.
    4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
      Screen Shot 2022-11-16 at 4 42 54 PM
      Format:
    Contributor details
    Your Expensify account email: <REPLACE EMAIL HERE>
    Upwork Profile Link: <REPLACE LINK HERE>
    
  12. BackendArchitectX commented on Aug 18, 2026

    @BackendArchitectX

    Contributor details
    Your Expensify account email: pranayp.kadu@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~01d916f91d80c7d23a

  13. 32 remaining items

  14. 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
  15. melvin-bot commented on Oct 6, 2026

    @melvin-bot

    @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]

  16. mukhrr commented on Oct 9, 2026

    @mukhrr
    Contributor

    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)

    @mallenexpensify it's 3 days since this got to production.

  17. melvin-bot commented on Oct 12, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  18. melvin-bot commented on Oct 12, 2026

    @melvin-bot

    Payment Summary

    Resolving PRs:

    BugZero Checklist (@mallenexpensify)

    • I have confirmed assignees, roles, and Upwork contracts look correct
    • I have paid out Upwork contracts / manual NewDot requests
    • [BugZero Assignee] I have created a GH issue for creating/updating the regression test once above steps have been agreed upon
  19. mallenexpensify commented on Oct 15, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @thelullabyy due $250 via NewDot

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

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

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething 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