Split out of #894 for follow-up. The navigation half of #894 is fixed in #895 and the filename half in #896 — this is the remaining, still-open part, and it is the likely cause of the original "tapping the suggested Strand tag returns Keine Ergebnisse" report.
Problem
The search palette can list two entries with the same visible name and no way to distinguish them. Picking the wrong one returns "No results".
The reporter of #894 saw exactly this: their third screenshot shows "Strand" listed twice under Tags. I originally dismissed that as cosmetic. It isn't — it's the defect.
Why duplicates appear
Tags in Immich are per-user (tag.userId; uniqueness is enforced per user via TagRepository.getByValue(userId, value)). Two users can each own a tag named Strand with different ids.
TagRepository.getAll(userId) deliberately returns own tags plus tags reachable through a shared space:
getAll(userId: string) {
return this.db
.selectFrom('tag')
.select(columns.tag)
.where((eb) => this.ownedOrSpaceAccessible(eb, userId))
.orderBy('value')
.execute();
}
That scoping is correct and intentional for the tag explorer. The problem is what the palette does with the result.
Failure shape — common in setups where one admin account owns all externally-mounted libraries and everyone else is a Space member:
- the member owns a tag
Strand with zero assets
- the admin owns a tag
Strand carrying all the photos
- both appear in the palette, rendered identically
- picking the member's own one → "No results"
A second, independent source of visual duplicates: tag-row.svelte renders only item.name, not item.value. A nested tag travel/japan and a top-level japan therefore both render as "japan".
Contributing factors
- No owner/space disambiguation in the row —
tag-row.svelte renders {item.name} and nothing else.
- No nesting context —
value (the full path) is available but unused, so tags at different paths collide visually.
- No emptiness signal — a tag with no assets the viewer can access is suggested identically to one with thousands.
- The provider matches on
name and slices to 5 (global-search-manager.svelte.ts, tags provider), so two same-named tags can consume two of the five slots without either being useful.
Related observation (may be the same root cause)
Reported separately during #896 validation: tapping the "Tags" section header (not an individual entry) produced a filter chip literally labelled Tags and "No results". Unconfirmed which element was actually hit — every palette section heading is a non-interactive Command.GroupHeading, so this needs the post-tap URL to diagnose:
/photos?tags=<uuid> → a real tag named "Tags" with no accessible assets — same class of bug as above
/photos?tags=Tags → a non-uuid is being passed as a tag id — a distinct bug
active-filters-bar renders tagNames.get(tagId) ?? tagId, so a chip showing a category-like word is consistent with either.
Possible approaches (not yet decided)
- Disambiguate in the row — show the full
value path and/or an owner/space badge when two visible entries share a name
- De-duplicate by name in the provider, preferring the tag that actually has accessible assets
- Suppress empty tags — filter out tags with no viewer-accessible assets (needs a count the palette doesn't currently fetch; cost to be assessed against the existing 20k tag-cache ceiling)
- Merge semantics — treat same-named tags as one filter and pass all matching ids
Worth deciding deliberately: options 2 and 3 change what the palette shows, and hiding a user's own tag entirely could be surprising in the other direction.
Repro
Requires two accounts (invisible to a library owner testing alone):
- Admin owns the assets and a tag
Strand applied to them; shares them into a Space.
- Second user is a member of that Space and has their own tag named
Strand with no assets.
- As the member, open the search palette and type
Strand.
- Two identical "Strand" rows appear. Picking the member's own one returns "No results".
Split out of #894 for follow-up. The navigation half of #894 is fixed in #895 and the filename half in #896 — this is the remaining, still-open part, and it is the likely cause of the original "tapping the suggested Strand tag returns Keine Ergebnisse" report.
Problem
The search palette can list two entries with the same visible name and no way to distinguish them. Picking the wrong one returns "No results".
The reporter of #894 saw exactly this: their third screenshot shows "Strand" listed twice under Tags. I originally dismissed that as cosmetic. It isn't — it's the defect.
Why duplicates appear
Tags in Immich are per-user (
tag.userId; uniqueness is enforced per user viaTagRepository.getByValue(userId, value)). Two users can each own a tag namedStrandwith different ids.TagRepository.getAll(userId)deliberately returns own tags plus tags reachable through a shared space:That scoping is correct and intentional for the tag explorer. The problem is what the palette does with the result.
Failure shape — common in setups where one admin account owns all externally-mounted libraries and everyone else is a Space member:
Strandwith zero assetsStrandcarrying all the photosA second, independent source of visual duplicates:
tag-row.svelterenders onlyitem.name, notitem.value. A nested tagtravel/japanand a top-leveljapantherefore both render as "japan".Contributing factors
tag-row.svelterenders{item.name}and nothing else.value(the full path) is available but unused, so tags at different paths collide visually.nameand slices to 5 (global-search-manager.svelte.ts, tags provider), so two same-named tags can consume two of the five slots without either being useful.Related observation (may be the same root cause)
Reported separately during #896 validation: tapping the "Tags" section header (not an individual entry) produced a filter chip literally labelled
Tagsand "No results". Unconfirmed which element was actually hit — every palette section heading is a non-interactiveCommand.GroupHeading, so this needs the post-tap URL to diagnose:/photos?tags=<uuid>→ a real tag named "Tags" with no accessible assets — same class of bug as above/photos?tags=Tags→ a non-uuid is being passed as a tag id — a distinct bugactive-filters-barrenderstagNames.get(tagId) ?? tagId, so a chip showing a category-like word is consistent with either.Possible approaches (not yet decided)
valuepath and/or an owner/space badge when two visible entries share a nameWorth deciding deliberately: options 2 and 3 change what the palette shows, and hiding a user's own tag entirely could be surprising in the other direction.
Repro
Requires two accounts (invisible to a library owner testing alone):
Strandapplied to them; shares them into a Space.Strandwith no assets.Strand.