Skip to content

[$175] Report fields - Disabled list type report field appears on report after deletion #103935

Open

Description

@applause-bot

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


Version Number: 9.5.8-3
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: #102142
Email or phone of affected tester (no customers): applausetester+bm8900011@applause.expensifail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Web, android
App Component: Workspace Settings

Action Performed:

  1. Go to staging.new.expensify.com
  2. Create a workspace
  3. Go to Workspace Settings > Report fields and create a list type report field
  4. Go to workspace chat and create an expense report
  5. Go to Workspace Settings > Report fields, open the list type report field, and disable all list options
  6. Go to workspace chat, open the report, and verify the report field does not appear
  7. Go to Workspace Settings > Report fields and delete the list type report field
  8. Navigate back to workspace chat and open the report

Expected Result:

A disabled list field that was hidden before deletion should stay hidden after deletion.

Actual Result:

Disabled list type report field appears on the report after deletion

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7285541_1791910709094.bandicam_2026-10-13_19-36-52-425.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Svetlana Lazutkina
Bug ID: 7285541

Upwork Automation - Do Not Edit

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Oct 13, 2026
  2. applause-bot commented on Oct 13, 2026

    @applause-bot
    Author

    While Applause doesn't embed attachments for this bug, here are the links for quick view:
    https://platform.applause.com/services/links/v1/external/da8709f0f0418e466630b17d5f5b02587650f8a38c0dc396e1a04700b38c8a42

  3. MelvinBot commented on Oct 13, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    Only the workspace's copy of a report field records which list options are disabled. The report keeps its own snapshot of the field, taken when the report was created, and that snapshot still shows every option as enabled.

    • getAvailableReportFields copies disabledOptions from the workspace field onto the report's copy, but only while the workspace field exists.
    • After you delete the field, policyReportField is undefined, so the report's stale disabledOptions are used.
    • shouldHideSingleReportField now finds enabled options, so the field shows again.

    This bug is older than Restore: Show up to three report fields per row and edit them inline, which did not touch this logic. That PR did add an inline delete button for fields that are no longer on the workspace, which makes the field that reappears easier to notice.

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

    In shouldHideSingleReportField, also hide a list field that is no longer on the workspace and has no value:

    1. Add a policyFieldList argument.
    2. Return true when the field type is list, the field is not in policyFieldList, and the field has no value.
    3. Pass policy?.fieldList from all three callers so they stay consistent:
    4. Add a unit test: given a deleted list field with no value, the field is hidden.

    A deleted field that still has a value stays visible, so the report keeps data a user entered.

    What alternative solutions did you explore? (Optional)

    • Filter in getAvailableReportFields. This is broader. It also changes the report title logic that reads this list.
    • Sync disabledOptions onto each report's copy. This would be exact, but the backend would have to write it when options change or the field is deleted. That work is outside the App repo.
    Supporting evidence
    • Workspace actions update only policy.fieldList, never a report's fieldList. This applies to both deleteReportFields (src/libs/actions/Policy/ReportField.ts, around lines 313-394) and updateReportFieldListValueEnabled (around lines 476-525).
    • While the delete is pending, the workspace field still exists, so the field stays hidden. It reappears once the success data sets the workspace field to null.
    • The comment in getAvailableReportFields says keeping deleted fields on the report is intended: "These will persist even if a field is deleted from the policy."
    • Confidence is medium. The code path is clear, but I did not reproduce the bug or check whether the report's copy has an empty value in this repro. If the backend fills in a default value, the "no value" check would not hide the field.

    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/103935) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Oct 13, 2026
  5. melvin-bot commented on Oct 13, 2026

    @melvin-bot

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

  6. changed the title [-]Report fields - Disabled list type report field appears on report after deletion[/-] [+][$175] Report fields - Disabled list type report field appears on report after deletion[/+] on Oct 13, 2026
  7. melvin-bot commented on Oct 13, 2026

    @melvin-bot
  8. janiya2 commented on Oct 13, 2026

    @janiya2

    Contributor details
    Your Expensify account email: janithad2@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/janithad
    Upwork Job URL: https://www.upwork.com/jobs/~022110094793846314623
    Upwork Job ID: 2110094793846314623

    Proposal

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

    When a workspace admin disables all list options of a list-type report field and then deletes that report field from Workspace Settings, the report field (which was correctly hidden on an expense report while all its options were disabled) unexpectedly reappears on the report after the field is deleted from the workspace.

    What is the root cause of that problem?

    1. When options of a list report field are disabled in Workspace Settings (updateReportFieldListValueEnabled), only policy.fieldList[fieldKey].disabledOptions is updated in Onyx. The report's snapshot in report.fieldList retains its initial state where options were not recorded as disabled.
    2. While the field still exists on the workspace policy:
      • getAvailableReportFields merges disabledOptions: policyReportField.disabledOptions from the policy onto the report's copy of the field:
        if (field) {
            return {
                ...field,
                ...(policyReportField
                    ? {
                          disabledOptions: policyReportField.disabledOptions,
                          values: policyReportField.values,
                      }
                    : {}),
            };
        }
      • In hasEnabledListValue:
        function hasEnabledListValue(reportField: PolicyReportField): boolean {
            return reportField.values.some((_, index) => !reportField.disabledOptions.at(index));
        }
      • Because all options are disabled in policyReportField.disabledOptions, hasEnabledListValue(reportField) returns false.
      • Consequently, shouldHideSingleReportField evaluates !hasEnableOption to true, and the field is properly hidden on the report.
    3. However, once the report field is deleted from the workspace:
      • policyReportField becomes undefined.
      • In getAvailableReportFields, the spread ...(policyReportField ? ... : {}) does not run.
      • As a result, reportField.disabledOptions falls back to the report's local copy, where disabledOptions is undefined (or empty).
      • In hasEnabledListValue, !reportField.disabledOptions.at(index) evaluates !undefined to true, erroneously concluding that all list options are enabled!
      • Therefore, shouldHideSingleReportField returns false (do not hide), and the deleted list field unexpectedly unhides and reappears on the report with an inline delete button.

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

    The root issue should be resolved centrally in src/libs/ReportUtils.ts within getAvailableReportFields.

    All downstream consumers (MoneyRequestViewReportFields.tsx, MoneyReportView.tsx, and hasFieldViolations in ReportUtils.ts) obtain their fields through getReportFieldMaps, which calls getAvailableReportFields.

    Primary Recommended Approach (Single-Point Central Fix in getAvailableReportFields):

    In src/libs/ReportUtils.ts, inside getAvailableReportFields:
    When a field is present in report.fieldList (field) but no longer exists on the policy (!policyReportField), if it is a list type field that has no selected value (!field.value), it should not be returned for display on unsettled reports:

    const fields = mergedFieldIds.map((id) => {
        const field = report?.fieldList?.[getReportFieldKey(id)] ?? report?.fieldList?.[id];
        const policyReportField = policyReportFields.find(({fieldID}) => fieldID === id);
    
        if (field) {
            // If a list-type report field has been deleted from the policy and has no selected value,
            // it cannot be selected and should not reappear on the report.
            if (!policyReportField && field.type === CONST.REPORT_FIELD_TYPES.LIST && !field.value) {
                return null;
            }
    
            return {
                ...field,
                ...(policyReportField
                    ? {
                          disabledOptions: policyReportField.disabledOptions,
                          values: policyReportField.values,
                      }
                    : {}),
            };
        }
    
        if (policyReportField) {
            return policyReportField;
        }
    
        return null;
    });

    Why this is the cleanest solution:

    • If a list field was deleted from the policy, no valid options can ever be selected for it on the workspace.
    • If the report already has an entered value (field.value), it will still be returned and preserved historically as intended.
    • If the field was deleted without any value entered (or was already disabled and hidden), it cleanly remains hidden.
    • It does NOT require modifying function signatures or prop drilling policyFieldList across MoneyRequestViewReportFields.tsx, MoneyReportView.tsx, and ReportUtils.ts.

    Unit Tests:

    Add unit tests in tests/unit/ReportUtilsTest.ts:

    1. Verify that getAvailableReportFields excludes an unsettled report's list field when the field is deleted from policyReportFields and has no value (value: '').
    2. Verify that getAvailableReportFields retains a deleted list field if it has a populated historical value (value: 'Option 1').

    What alternative solutions did you explore? (Optional)

    1. Passing policyFieldList to shouldHideSingleReportField:
      Modifying shouldHideSingleReportField(reportField, policyFieldList) and checking !policyFieldList?.[fieldKey] && !reportField.value.
      Drawback: This modifies the function signature and requires updating all 3 caller sites (MoneyRequestViewReportFields.tsx:150, MoneyReportView.tsx:132, and ReportUtils.ts:10718), introducing unnecessary code duplication across components. Handling it at the source in getAvailableReportFields is far cleaner and adheres strictly to DRY principles.

    2. Syncing disabledOptions to all report snapshots upon policy updates:
      Drawback: Modifying all active report snapshots on the client or backend whenever an admin toggles a list option in workspace settings would be expensive, non-performant, and out-of-scope for the App repository.


    Upwork Proposal / Cover Letter (For Upwork Submission)

    Hi,

    I have thoroughly investigated Expensify issue #103935 ("Report fields - Disabled list type report field appears on report after deletion") and identified the exact root cause in src/libs/ReportUtils.ts.

    Summary of Root Cause & Fix:

    • When options of a list report field are disabled in Workspace Settings, only policy.fieldList[fieldKey].disabledOptions is updated in Onyx, while the report's snapshot retains an un-updated state.
    • Once the field is deleted from the workspace, policyReportField becomes undefined. In getAvailableReportFields, falling back to the report copy results in disabledOptions being undefined. Consequently, hasEnabledListValue evaluates !undefined to true, causing shouldHideSingleReportField to return false and unexpectedly unhide the deleted list field.
    • I have prepared a clean, single-point architectural fix centrally within getAvailableReportFields in ReportUtils.ts, ensuring that deleted list fields without a selected value (!field.value) are excluded from unsettled reports, without requiring multi-file prop-drilling or breaking caller signatures.

    My full technical proposal and unit test plan are detailed on the GitHub issue:
    #103935

    GitHub Username: janiya2
    Expensify Email: janithad2@gmail.com
    Upwork Profile: https://www.upwork.com/freelancers/janithad

    Ready to implement the PR and automated unit tests immediately upon assignment.

    Best regards,
    Janitha Dilshan

  9. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  10. jloa-dev commented on Oct 13, 2026

    @jloa-dev

    Proposal

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

    When a list-type report field has all of its options disabled on a workspace, it is correctly hidden on unsettled expense reports. However, once the report field is deleted from the workspace settings, the disabled field unexpectedly reappears on the unsettled report with an empty value.

    What is the root cause of that problem?

    The root cause lies in how src/libs/ReportUtils.ts:getAvailableReportFields reconciles policy field definitions with the report's snapshot:

    1. Option Disabling Updates Policy Only:
      When an admin disables all list options in Workspace Settings > Report Fields, Onyx updates policy.fieldList[fieldKey].disabledOptions to [true, true, ...]. Existing report snapshots (report.fieldList) are not mutated, so report.fieldList[fieldKey].disabledOptions remains undefined.

    2. Snapshot Overlay in getAvailableReportFields (lines 5190–5203):

      if (field) {
          return {
              ...field,
              ...(policyReportField
                  ? {
                        disabledOptions: policyReportField.disabledOptions,
                        values: policyReportField.values,
                    }
                  : {}),
          };
      }
      • While the field still exists on the policy, policyReportField is present, so disabledOptions is overlaid onto field.
      • In hasEnabledListValue(reportField) (lines 14424–14426):
        function hasEnabledListValue(reportField: PolicyReportField): boolean {
            return reportField.values.some((_, index) => !reportField.disabledOptions.at(index));
        }
        Because all options in policyReportField.disabledOptions are true, hasEnabledListValue returns false. shouldHideSingleReportField evaluates !hasEnableOption to true and hides the field.
    3. Regression Upon Deletion:

      • When the field is deleted from the workspace, policyReportFields no longer contains the field (policyReportField === undefined).
      • In getAvailableReportFields, the spread ...(policyReportField ? ... : {}) evaluates to {}.
      • Consequently, field.disabledOptions reverts to the report snapshot's local value (undefined).
      • In hasEnabledListValue, !reportField.disabledOptions.at(index) evaluates !undefined to true, mistakenly considering every option enabled.
      • shouldHideSingleReportField now returns false (do not hide), causing the deleted, unselectable list field to resurrect on the unsettled report.

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

    In src/libs/ReportUtils.ts:

    1. Cleanly Filter Unpopulated Deleted Fields in getAvailableReportFields:
      For unsettled reports, a report field that was completely deleted from the workspace policy (!policyReportField) and has no entered value (!field.value?.trim()) cannot have options selected or edited, and should not be displayed:

      const fields = mergedFieldIds.map((id) => {
          const field = report?.fieldList?.[getReportFieldKey(id)] ?? report?.fieldList?.[id];
          const policyReportField = policyReportFields.find(({fieldID}) => fieldID === id);
      
          if (field) {
              // If a field has been deleted from the policy and has no entered value on an unsettled report,
              // it cannot be filled and should remain hidden.
              if (!policyReportField && !field.value) {
                  return null;
              }
      
              return {
                  ...field,
                  ...(policyReportField
                      ? {
                            disabledOptions: policyReportField.disabledOptions,
                            values: policyReportField.values,
                        }
                      : {}),
              };
          }
      
          if (policyReportField) {
              return policyReportField;
          }
      
          return null;
      });
    2. Preserve Historical Values on Settled / Populated Reports:

      • If a field was previously populated by the user before deletion (field.value is present), !field.value is false, so the field is preserved in the list for auditability and historical record.
      • If the report is settled (reportIsSettled === true), lines 5181–5183 already return reportFields directly.
      • Only unpopulated, deleted fields on unsettled reports are omitted, matching expected design behavior.

    What alternative solutions did you explore? (Optional)

    • Prop-drilling policyFieldList into shouldHideSingleReportField: Requires changing signatures across MoneyRequestViewReportFields.tsx, MoneyReportView.tsx, and ReportUtils.ts. Resolving it centrally at the source in getAvailableReportFields adheres to DRY principles and eliminates redundant prop forwarding.
    • Mutating all active reports on the backend upon policy deletion: Expensive and out of scope for frontend state reconciliation.

    Test Plan

    Automated Unit Tests (tests/unit/ReportUtilsTest.ts):

    1. Create an unsettled report with a list field in report.fieldList that is missing from policyReportFields.
    2. When field.value is empty, assert that getAvailableReportFields returns an array excluding the deleted field.
    3. When field.value is 'Option 1', assert that getAvailableReportFields retains the deleted field with its value intact.
    4. When reportIsSettled is true, assert that all snapshot fields continue to be returned.

    Manual QA Steps:

    1. Navigate to Workspace Settings > Report Fields and create a list type report field with options.
    2. Open workspace chat and create a new expense report.
    3. In Workspace Settings > Report Fields, disable all options for that list field.
    4. Return to the expense report and verify the list field is hidden.
    5. In Workspace Settings > Report Fields, delete the list field.
    6. Return to the expense report.
    7. Verify: The deleted list field remains hidden and does not reappear on the report.
    8. Create another expense report and select a value for a list report field. Delete the field from workspace settings.
    9. Verify: The entered value is still displayed on the expense report historically.

    Contributor details
    Your Expensify account email: jloa.dev@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~0163955a2ba22190aa

  11. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  12. camesenin commented on Oct 13, 2026

    @camesenin

    Contributor details
    Your Expensify account email: camesenin@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~0170d6b02e9440e604

    Proposal

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

    When a list type report field has all of its options disabled in Workspace Settings, it is correctly hidden on unsettled expense reports. However, if the workspace admin subsequently deletes that list report field from the workspace, the field unexpectedly reappears on the report with a delete (trash) button.

    What is the root cause of that problem?

    This is a regression introduced by PR #102142 ("Restore: Show up to three report fields per row and edit them inline"), which added inline delete buttons for report fields that were deleted from the workspace so that historical entered values wouldn't be locked without an editor.

    1. When options of a list report field are disabled in Workspace Settings, the update modifies policy.fieldList[fieldKey].disabledOptions in Onyx. The report snapshot (report.fieldList) is not updated across all reports.
    2. While the field exists on the policy:
      • In src/libs/ReportUtils.ts (getAvailableReportFields), disabledOptions is merged onto the report's copy:
        if (field) {
            return {
                ...field,
                ...(policyReportField
                    ? {
                          disabledOptions: policyReportField.disabledOptions,
                          values: policyReportField.values,
                      }
                    : {}),
            };
        }
      • In hasEnabledListValue(reportField):
        function hasEnabledListValue(reportField: PolicyReportField): boolean {
            return reportField.values.some((_, index) => !reportField.disabledOptions.at(index));
        }
        Because all options are disabled in policyReportField.disabledOptions, hasEnabledListValue returns false.
      • In shouldHideSingleReportField: !hasEnableOption evaluates to true, and the field is correctly hidden.
    3. Once the field is deleted from the workspace:
      • policyReportField becomes undefined.
      • In getAvailableReportFields, reportField.disabledOptions is taken from report.fieldList, where disabledOptions is undefined (or empty).
      • In hasEnabledListValue, !reportField.disabledOptions.at(index) evaluates !undefined to true, erroneously assuming all options are enabled.
      • Consequently, shouldHideSingleReportField returns false (do not hide).
      • In MoneyRequestViewReportFields.tsx, isFieldDeletable: field.fieldID !== CONST.REPORT_FIELD_TITLE_FIELD_ID && !isFieldOnPolicy && isEditable evaluates to true, resurrecting the deleted list field with an inline delete button.

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

    The fix should be handled centrally in src/libs/ReportUtils.ts inside getAvailableReportFields.

    When a field is present in report.fieldList (field) but no longer exists on the policy (!policyReportField), if it is a list type field that has no selected value (!field.value), it has no valid selectable options and must not be resurrected on unsettled reports:

    const fields = mergedFieldIds.map((id) => {
        const field = report?.fieldList?.[getReportFieldKey(id)] ?? report?.fieldList?.[id];
        const policyReportField = policyReportFields.find(({fieldID}) => fieldID === id);
    
        if (field) {
            // If a list-type report field was deleted from the policy and has no selected value,
            // it cannot be selected and should not reappear on unsettled reports.
            if (!policyReportField && field.type === CONST.REPORT_FIELD_TYPES.LIST && !field.value) {
                return null;
            }
    
            return {
                ...field,
                ...(policyReportField
                    ? {
                          disabledOptions: policyReportField.disabledOptions,
                          values: policyReportField.values,
                      }
                    : {}),
            };
        }
    
        if (policyReportField) {
            return policyReportField;
        }
    
        return null;
    });

    Why this is the cleanest solution:

    • If an admin deletes a list field from the workspace, no new values can ever be selected for it.
    • If an expense already held an entered value (field.value), it is retained and rendered with the trash button so the historical value is not lost, strictly fulfilling PR Restore: Show up to three report fields per row and edit them inline #102142's intended behavior.
    • If the field has no entered value (e.g. was disabled and hidden prior to deletion), it cleanly remains hidden.
    • It does not require prop-drilling or modifying caller signatures across MoneyRequestViewReportFields.tsx, MoneyReportView.tsx, or hasFieldViolations.

    Unit Tests:

    Add unit test coverage in tests/unit/ReportUtilsTest.ts:

    1. Verify that getAvailableReportFields excludes an unsettled report's list field when the field is deleted from policyReportFields and has no value (value: '').
    2. Verify that getAvailableReportFields retains a deleted list field if it has a populated historical value (value: 'Option 1').

    What alternative solutions did you explore? (Optional)

    1. Passing policyFieldList to shouldHideSingleReportField:
      Modifying shouldHideSingleReportField(reportField, policyFieldList) and checking !policyFieldList?.[fieldKey] && !reportField.value.
      Drawback: Modifies the function signature across multiple call sites (MoneyRequestViewReportFields.tsx, MoneyReportView.tsx, and ReportUtils.ts), violating DRY. Handling it at the source in getAvailableReportFields is far cleaner.
    2. Syncing disabledOptions across all report snapshots on policy updates:
      Drawback: Updating all active reports in Onyx when options are toggled would introduce heavy performance overhead and network operations.
  13. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  14. khizzcr7 commented on Oct 14, 2026

    @khizzcr7

    Hi, I'd like to work on this.

    Root cause: getAvailableReportFields in src/libs/ReportUtils.ts reconciles the report's snapshot of a report field against the workspace's live definition, copying disabledOptions from the workspace field onto the report's copy only while the workspace field still exists. After the field is deleted from Workspace Settings, policyReportField is undefined, so the reconciliation falls back to the report's stale snapshot where all options read as enabled, and the field reappears on the report.

    Fix approach:

    1. In getAvailableReportFields, when the workspace-side field definition is missing (deleted) but a report-side snapshot exists, preserve the previously applied disabled/hidden state instead of resetting to all-enabled - e.g. carry over disabledOptions from the last known snapshot or exclude the field when it was hidden before deletion.
    2. Alternatively, clear the report field value entry when its workspace definition is deleted so the field has nothing to render.
    3. Keep behavior unchanged for fields that still exist on the workspace.

    Verification: (1) Follow the exact repro: create list field, disable all options, confirm hidden on report, delete the field, confirm it stays hidden on the report. (2) Regression: a non-disabled list field that is deleted behaves the same as before. (3) Existing report-fields unit tests still pass.

  15. khizzcr7 commented on Oct 14, 2026

    @khizzcr7

    Contributor details
    Your Expensify account email: khizar.akram.ka@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~01c5c6bc0e7d162f7b

  16. melvin-bot commented on Oct 14, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  17. GhulamMustufa commented on Oct 14, 2026

    @GhulamMustufa

    Contributor details
    Your Expensify account email: ghulammustafa.mf@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~01f39a67fa921946bd

    Proposal

    What is the root cause of that problem?

    When report fields are configured in Workspace Settings, option states (such as disabledOptions) are stored on the policy's field definition in Onyx (policy.fieldList), while individual reports retain a local snapshot (report.fieldList) from when the report was created.

    1. While the list field exists on the policy:
      Inside getAvailableReportFields in src/libs/ReportUtils.ts, the policy's live disabledOptions array is merged onto the report field snapshot. When all options are disabled, hasEnabledListValue checks disabledOptions and returns false, so shouldHideSingleReportField correctly hides the field on expense reports.

    2. Once the list field is deleted from the workspace:
      policyReportField becomes undefined in getAvailableReportFields. The function falls back to the report's snapshot in report.fieldList, where disabledOptions is empty or undefined.
      In hasEnabledListValue, checking indices against an empty or undefined disabledOptions array evaluates as enabled (!undefined === true). Consequently, shouldHideSingleReportField returns false, causing the deleted field to unexpectedly reappear on the report with an inline delete button.

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

    In src/libs/ReportUtils.ts within getAvailableReportFields:

    When constructing the list of available fields for an unsettled report, check if a field originates from the report's snapshot but no longer exists on the policy (!policyReportField). If the field is a list type (CONST.REPORT_FIELD_TYPES.LIST) and has no entered value (!field.value), omit it from the returned available fields.

    This ensures:

    • If a list field was deleted from the workspace and has no entered value (such as a field that was previously disabled and hidden), it cleanly remains hidden on unsettled reports.
    • If a report already had a historical value selected for that field, the field remains visible so the user can inspect or delete the historical entry (preserving the intended behavior of PR Restore: Show up to three report fields per row and edit them inline #102142).
    • Settled reports remain unaffected as they continue to return their persisted reportFields.

    We will also add unit tests in tests/unit/ReportUtilsTest.ts:

    1. Verify that getAvailableReportFields omits a list-type field that was deleted from the policy when it has no value.
    2. Verify that getAvailableReportFields retains a deleted list-type field if a value was previously saved.

    What alternative solutions did you explore? (Optional)

    1. Modifying shouldHideSingleReportField across call sites to inspect whether the field exists on the policy. This was rejected because it requires prop-drilling policy fields through multiple components (MoneyRequestViewReportFields, MoneyReportView, and violation utilities), whereas handling it centrally in getAvailableReportFields solves the problem cleanly at the data source.
    2. Broadcasting updates to all report snapshots whenever workspace field options are toggled. This was rejected due to unnecessary Onyx write overhead and network complexity.
  18. melvin-bot commented on Oct 14, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  19. emkhalid commented on Oct 14, 2026

    @emkhalid
    Contributor

    @mkhutornyi I was able to reproduce this and identified why MelvinBot's 4-file proposal is unnecessarily invasive:

    • Melvin's premise is mistaken: Melvin avoided fixing getAvailableReportFields claiming it affects report title logic. In reality, report title logic explicitly filters for isReportFieldOfTypeTitle (type: 'text'), which is never a list field. Title logic is completely unaffected.
    • The real root cause: When options are disabled, only policy.fieldList is updated in Onyx. Once deleted from the policy, policyReportField becomes undefined in getAvailableReportFields. The fallback to the report's snapshot leaves disabledOptions undefined, which hasEnabledListValue treats as enabled, unhiding the field.

    I have a clean 1-file fix directly in getAvailableReportFields (ReportUtils.ts) that excludes deleted list fields without a value on unsettled reports. It solves the issue centrally at the source without prop-drilling policyFieldList across 4 files or changing function signatures.

    Repro Demo:

    reproduceDemoReport.webm

    Fix Demo:

    fixDemoReport.webm

    Let me know if you'd like me to submit the full proposal!

  20. GhulamMustufa commented on Oct 15, 2026

    @GhulamMustufa

    @mkhutornyi Thanks a lot @emkhalid for testing this and sharing the demo videos!

    It's great to see confirmation that handling this in getAvailableReportFields (as outlined in the proposal above) resolves the issue cleanly in a single file without affecting report titles.

    Looking forward to your review @mkhutornyi, and happy to get started on the PR and unit tests whenever you're ready!

  21. melvin-bot commented on Oct 16, 2026

    @melvin-bot

    @mkhutornyi Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

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.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorOverdue

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions