Skip to content

[$250] Advanced Tag filter: mixed 'No tag' + real tag selection does not return both untagged and tagged expenses #92604

Closed

Description

@MelvinBot

Problem

When the advanced Tag filter is used with a mixed selection of No tag together with one or more real tags (e.g. No tag + Test), the filter does not behave like an expected OR-style multi-select filter. It does not return both untagged expenses and expenses with the selected tag.

This is a follow-up to Expensify/App#91083 / PR #91652, which fixed the standalone No tag case. The mixed case was deliberately left out of scope. See the original investigation: #91652 (comment) and the request to split it out: #91652 (comment)

Root cause

No tag and real tags are represented by different query concepts:

  • A real tag is represented as tag:<tag name>.
  • An untagged expense is represented as -has:tag.

PR #91652 fixes the standalone No tag case by converting tag: [none] into -has:tag. However, the mixed case still falls through to the existing comma-list tag serializer, so tag: [none, Test] becomes tag:none,Test (a literal tag named none), which is incorrect.

This mixed behavior is not newly introduced by #91652 — before that PR, all selected tag values were serialized through the same generic tag:<comma-list> path, so No tag + Test was already represented as tag:none,Test.

For comparison, the Category filter has a similar empty-value option but works because "No category" lives inside the category: value domain (e.g. category:none,Uncategorized,<real category>). Tag behaves differently because the correct representation for missing tags is -has:tag, a different filter key/operator from tag:<tag name>.

Why this is non-trivial

A complete fix for the mixed tag case would likely need either:

  1. search query/query-language support for an OR expression like (-has:tag) OR tag:Test, or
  2. explicit support for treating tag:none,Test as "missing tag OR tag Test".

A client-only workaround like emitting -has:tag tag:Test would be incorrect because separate filters are combined as AND, so it would mean "has no tag AND tag is Test". Running two searches and merging client-side would also be risky because it could break pagination, totals, hashes, bulk actions, and select-all-matching behavior.

Expected behavior

Selecting No tag together with one or more real tags should return both untagged expenses and expenses matching the selected tag(s) (OR semantics), consistent with how other multi-select filters behave.

Reproduction steps

  1. Have at least one expense with a tag (e.g. Test) and at least one expense with no tag.
  2. Go to Spend > Expenses.
  3. Open Filters and set Tag to No tag and an existing tag (e.g. Test).
  4. Save and view results.
  5. Actual: untagged expenses and Test-tagged expenses are not both returned.
  6. Expected: both untagged expenses and Test-tagged expenses are returned.

A screen recording of the behavior is in #91652 (comment)

Issue OwnerCurrent Issue Owner: @KJ21-ENG
Upwork Automation - Do Not Edit

Activity

  1. MelvinBot commented on Jun 3, 2026

    @MelvinBot
    ContributorAuthor

    Follow-up to #91083 / PR #91652, requested by Beamanator in #91652 (comment).

    Beamanator is assigned. I wasn't able to auto-assign @KJ21-ENG (GitHub only allows assigning collaborators or users who've already commented on this issue) — @KJ21-ENG, please self-assign, or someone with triage access can add you.

  2. KJ21-ENG commented on Jun 3, 2026

    @KJ21-ENG
    Contributor

    🫡

  3. KJ21-ENG commented on Jun 3, 2026

    @KJ21-ENG
    Contributor

    @Beamanator Please add me here so i can start working on this and raise a PR. Thanks!

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    BugSomething is broken. Auto assigns a BugZero manager.
    on Jun 4, 2026
  5. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Jun 4, 2026
  6. melvin-bot commented on Jun 4, 2026

    @melvin-bot

    Current assignee @ShridharGoel is eligible for the External assigner, not assigning anyone new.

  7. changed the title [-]Advanced Tag filter: mixed 'No tag' + real tag selection does not return both untagged and tagged expenses[/-] [+][$250] Advanced Tag filter: mixed 'No tag' + real tag selection does not return both untagged and tagged expenses[/+] on Jun 4, 2026
  8. melvin-bot commented on Jun 4, 2026

    @melvin-bot
  9. removed
    Help WantedApply this label when an issue is open to proposals by contributors
    on Jun 4, 2026
  10. Beamanator commented on Jun 4, 2026

    @Beamanator
    Contributor

    Added! @ShridharGoel @KJ21-ENG can you both please first confirm the OP of this issue is accurate before we move forward with a proposal & PR?

  11. 18 remaining items

  12. Beamanator commented on Jun 5, 2026

    @Beamanator
    Contributor

    Good question — checked, and yeah you're right. -has:tag and "No tag" inside the selector both already exist, same as Category. I believe the bug is just that App converts standalone "No tag" to -has:tag while Category cleanly emits category:none in every case.

  13. Beamanator commented on Jun 5, 2026

    @Beamanator
    Contributor

    does that sound about right @ShridharGoel

  14. KJ21-ENG commented on Jun 5, 2026

    @KJ21-ENG
    Contributor

    Hmm, If collision risk with a real tag named 'none' is acceptable then this sounds right to me.

    The inconsistency is that Category keeps No category as category:none, while Tag special-cases standalone No tag to -has:tag. To align Tag with Category, App should use tag:none for No tag including mixed No tag + real tag, while backend/search should treat tag:none,<real tags> as missing-tag OR selected tags. We should keep -has:tag supported for backward compatibility.

    cc:- @Beamanator @ShridharGoel @trjExpensify

  15. ShridharGoel commented on Jun 5, 2026

    @ShridharGoel
    Contributor

    I'm not able to repro this on staging, @KJ21-ENG can you check?

  16. KJ21-ENG commented on Jun 5, 2026

    @KJ21-ENG
    Contributor

    I'm not able to repro this on staging, @KJ21-ENG can you check?

    Doesn't seem to be reproducing on staging. 👀

    cc:- @ShridharGoel @Beamanator @trjExpensify

    ezyZip.12.mp4
  17. melvin-bot commented on Jun 9, 2026

    @melvin-bot

    @KJ21-ENG Whoops! This issue is 2 days overdue. Let's get this updated quick!

  18. melvin-bot commented on Jun 11, 2026

    @melvin-bot

    @KJ21-ENG Eep! 4 days overdue now. Issues have feelings too...

  19. KJ21-ENG commented on Jun 11, 2026

    @KJ21-ENG
    Contributor

    @ShridharGoel @Beamanator should we close this issue for now as it's not getting reproduced anymore? 👀

  20. melvin-bot commented on Jun 15, 2026

    @melvin-bot

    @KJ21-ENG Whoops! This issue is 2 days overdue. Let's get this updated quick!

  21. Beamanator commented on Jun 16, 2026

    @Beamanator
    Contributor

    GOod call closing this out!

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

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributor

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions