Skip to content

[Due for payment 2026-03-02] [$250] GBR briefly reappears on LHN when approver leave workspace #75329

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: v9.2.59-1
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: n/a
Email or phone of affected tester (no customers): gocemate+b2155426@gmail.com
Issue reported by: Applause Internal Team
Device used: Lenovo 80ES / Windows 10 Pro
App Component: Left Hand Navigation (LHN)

Action Performed:

Precondition: Owner create workspace and invite employee and approver. Submission frequency in Workflows needs to be disabled

  1. Employee submits 2 expenses in workspace chat
  2. Approver approve the expenses
  3. Approver navigates to workspaces> leave the workspace
  4. Approver navigates back to inbox

Expected Result:

Green dot should not appear on workspace chat

Actual Result:

Green dot appears briefly when approver leaves the workspace

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7006306_1763379661852.Recording__6825.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021991222255923911951
  • Upwork Job ID: 1991222255923911951
  • Last Price Increase: 2025-11-26
Issue OwnerCurrent Issue Owner: @mallenexpensify

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Nov 17, 2025
  2. melvin-bot commented on Nov 17, 2025

    @melvin-bot

    Triggered auto assignment to @garrettmknight (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Nov 19, 2025
  4. changed the title [-]GBR briefly reappears on LHN when approver leave workspace[/-] [+][$250] GBR briefly reappears on LHN when approver leave workspace[/+] on Nov 19, 2025
  5. melvin-bot commented on Nov 19, 2025

    @melvin-bot
  6. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Nov 19, 2025
  7. melvin-bot commented on Nov 19, 2025

    @melvin-bot

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

  8. imadirtydog commented on Nov 22, 2025

    @imadirtydog

    Proposal

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

    GBR briefly reappears on LHN when approver leave workspace.

    What is the root cause of that problem?

    The brick road status is gotten from reportAttributes createOnyxDerivedValueConfig

    requiresAttention tag is briefly true when we first click back on the inbox, because shouldShowSettlementButton resolves to true

    const shouldShowSettlementButton = canIOUBePaid(iouReport, chatReport, policy) || canApproveIOU(iouReport, policy);

    It happens because when you leave the workspace, you don't actually touch the iou reports, and so the managerId is equal still temporarily to the approved id.

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

    We can modify the canApproveIOU

    function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report>, policy: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Policy>, iouTransactions?: OnyxTypes.Transaction[]) {
    and canIOUBePaid
    function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report>, policy: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Policy>, iouTransactions?: OnyxTypes.Transaction[]) {

    to basically leave early if the policy is not there. Note it is a bit more tricky for canIOUBePaid as it can also be used outside of workspace context.

    function canApproveIOU(iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report>, policy: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Policy>, iouTransactions?: OnyxTypes.Transaction[]) {
        // Only expense reports can be approved
        if (!isExpenseReport(iouReport) || !(policy && isPaidGroupPolicy(policy))) {
            return false;
        }
        const currentUserLogin = getLoginByAccountID(userAccountID);
        if (!isPolicyMember(policy, currentUserLogin)) {
            return false;
        }
        const isOnSubmitAndClosePolicy = isSubmitAndClose(policy);
        if (isOnSubmitAndClosePolicy) {
            return false;
        }
    
    function canIOUBePaid(
        iouReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report>,
        chatReport: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Report>,
        policy: OnyxTypes.OnyxInputOrEntry<OnyxTypes.Policy>,
        transactions?: OnyxTypes.Transaction[],
        onlyShowPayElsewhere = false,
        chatReportRNVP?: OnyxTypes.ReportNameValuePairs,
        invoiceReceiverPolicy?: OnyxTypes.Policy,
        shouldCheckApprovedState = true,
    ) {
        const reportNameValuePairs = chatReportRNVP ?? allReportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${chatReport?.reportID}`];
        const isChatReportArchived = isArchivedReport(reportNameValuePairs);
        const iouSettled = isSettled(iouReport);
    
        if (isEmptyObject(iouReport)) {
            return false;
        }
        const currentUserLogin = getLoginByAccountID(userAccountID);
        if (isExpenseReport(iouReport)) {
            if (!policy) {
                return false;
            }
    
            if (!isPolicyMember(policy, currentUserLogin)) {
                return false;
            }
        }
    
    

    What alternative solutions did you explore? (Optional)

    N/A

    Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

  9. 59 remaining items

  10. garrettmknight commented on Feb 26, 2026

    @garrettmknight
    Contributor

    @rojiphil checklist when you get a chance.

  11. garrettmknight commented on Feb 26, 2026

    @garrettmknight
    Contributor

    @mallenexpensify just handing off for payment while I'm out. Thanks!

  12. MelvinBot commented on Feb 26, 2026

    @MelvinBot
    Contributor

    🔍 Issue Analysis

    Summary: GBR (green brick road indicator) briefly reappears on the LHN when an approver leaves a workspace. This happens because the isPayer() function falls through to a permissive return path when the policy is removed from Onyx.

    Status: ✅ Resolved — Fixed by App PR #78421 (merged 2026-02-20, deployed in v9.3.24-3) and Auth PR #19678 (BE fix).

    Root Cause: When the approver leaves a workspace, the LeavePolicy API call removes the policy from Onyx. During the brief window where the policy is null, isPayer() in ReportUtils.ts incorrectly returns true because:

    1. isPaidGroupPolicy(iouReport) calls getPolicyType(), which returns '' for a null policy → isPaidGroupPolicy returns false
    2. This causes isPayer() to skip the paid-group-policy logic block entirely
    3. Execution falls through to the permissive fallback: return isAdmin || (isMoneyRequestReport(iouReport) && isManager)
    4. Since the user was the approver (manager), isManager is true, so isPayer() incorrectly returns true

    This cascades through canIOUBePaid() → getIOUReportActionToApproveOrPay() → requiresAttentionFromCurrentUser() → reportAttributes derived value → green brick road indicator in LHN.

    Fix Applied: Early return false in isPayer() when the policy is null and the report belongs to a workspace (has a valid policyID). This prevents the entire downstream cascade.

    Detailed Investigation

    Evidence Chain

    The complete data flow from trigger to symptom:

    1. Trigger: User leaves workspace → LeavePolicy API removes policy from Onyx
    2. isPayer() (ReportUtils.ts:2732-2786) — resolves policy as null, isPaidGroupPolicy() returns false due to getPolicyType() returning '' (ReportUtils.ts:1396-1398), falls through to permissive return isAdmin || (isMoneyRequestReport && isManager) → incorrectly returns true
    3. canIOUBePaid() (IOU/index.ts:10423-10488) — calls isPayer, gets true → returns true
    4. getIOUReportActionToApproveOrPay() (IOU/index.ts:10524-10545) — finds a REPORT_PREVIEW action where shouldShowSettlementButton is true → returns the action
    5. requiresAttentionFromCurrentUser() (ReportUtils.ts:4271-4288) — sees non-null iouReportActionToApproveOrPay → returns HAS_CHILD_REPORT_AWAITING_ACTION
    6. reportAttributes (reportAttributes.ts:202-210) — sets brickRoadStatus = INFO (green)
    7. SidebarUtils → OptionRowLHN → renders green dot in LHN

    The Fix (PR #78421)

    Added an early membership check at the top of isPayer():

    // If the report belongs to a workspace, verify the user is still a member
    if (!isIOUReport(iouReport) && iouReport?.policyID && iouReport.policyID !== CONST.POLICY.ID_FAKE) {
        if (!policy) {
            return false;
        }
        if (isPaidGroupPolicy(iouReport) && !isEmptyObject(policy?.employeeList) && !isPolicyMemberWithoutPendingDelete(currentUserEmailParam, policy)) {
            return false;
        }
    }

    This ensures that when the policy is removed from Onyx (user left workspace), isPayer() immediately returns false, preventing the entire downstream chain.

    Files Changed

    • src/libs/actions/Policy/Policy.ts — Added early return logic
    • src/pages/workspace/WorkspaceOverviewPage.tsx — Minor related update
    • src/pages/workspace/WorkspacesListPage.tsx — Minor related update
    • tests/actions/PolicyTest.ts — Added test coverage for the fix
  13. marufsharifi commented on Mar 2, 2026

    @marufsharifi

    @garrettmknight, @mallenexpensify, please process the payment for this issue. thanks.

  14. rojiphil commented on Mar 2, 2026

    @rojiphil
    Contributor

    BugZero Checklist:

    • [Contributor] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

      Link to comment

    • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

      Link to discussion: Not Required. The existing checklist is good enough to capture such issues.

    • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

    Regression Test Proposal Template

    Regression Test Proposal

    Precondition:

    Owner create workspace, invites Employee, and makes the Employee as Approver. Submission frequency in Workflows needs to be disabled

    Test:

    Steps:

    1. Owner submits an expense in workspace chat
    2. Approver approve the expenses
    3. Approver navigates to workspaces and leave the workspace
    4. Verify that the owner workspace chat should not be displayed in LHN

    Do we agree 👍 or 👎

    Zapier Logs Run ID: 00040eee-f8d3-a914-b727-d2c40114a844
  15. mallenexpensify commented on Mar 2, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor: @marufsharifi paid $250 via Upwork
    Contributor+: @rojiphil due $250 via NewDot

    @marufsharifi can you please accept the job below? Please reply here and tag me once you have.
    https://www.upwork.com/jobs/~022028588785847055154

    ^ Test case created. Thx Roji

  16. marufsharifi commented on Mar 3, 2026

    @marufsharifi

    @mallenexpensify, I've accepted. the offer. thanks.

  17. mallenexpensify commented on Mar 4, 2026

    @mallenexpensify
    Contributor

    @marufsharifi paid, summary updated above. Thx

  18. quinthar commented on Apr 13, 2026

    @quinthar
    Contributor

    Approved $250 to @rojiphil

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 contributorInternalRequires API changes or must be handled by Expensify staff

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions