Skip to content

fix: surface a failed collections fetch instead of a false empty state (#6019) - #6131

Merged
atomantic merged 1 commit into
mainfrom
claim/issue-6019
Sep 3, 2026
Merged

fix: surface a failed collections fetch instead of a false empty state (#6019)#6131
atomantic merged 1 commit into
mainfrom
claim/issue-6019

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

On /media/collections, refresh() swallowed a rejected listMediaCollections() into []. A transient 500, network timeout, or DB blip therefore rendered the "No collections yet" onboarding empty state and handed every image and video in the library to the synthetic "Unsorted" bucket — reading as "all my collections were deleted." There was no error banner and no retry path; once the request() toast faded, nothing on screen said a read had failed.

The fix applies the repo's sentinel convention (AGENTS.md, "Sentinel + validate to distinguish 'not set / failed' from 'present-but-empty / valid'"):

  • collections is now null = never fetched / fetch failed, [] = the server genuinely has none.
  • The collections leg of the Promise.all resolves to an array on success and an { error } envelope on failure, so the failure survives the join as data instead of flattening into an indistinguishable [].
  • A failed read leaves collections alone — the sentinel on a first-load failure, the last good list on a later refresh — so the grid never blanks and the synthetic "Unsorted" bucket is skipped entirely rather than claiming the whole library is unfiled.
  • A persistent Banner tone="error" names the failure, states that nothing was deleted, and offers a Retry that re-invokes refresh() (disabled while a load is in flight).
  • The onboarding EmptyState now renders only on a genuine zero-collection response.
  • The fetch passes { silent: true }, per the client convention that a caller owning its own error UI suppresses the shared request() toast.

handleCreate / handleDelete no longer assume an array: they extend or filter only a list that actually loaded, so a mutation can't clear the sentinel and resurrect the "everything is unsorted" state.

Test plan

Four new tests in client/src/pages/MediaCollections.test.jsx, each verified to fail against the pre-fix component (4 failed / 20 passed) and pass after:

  • a rejected fetch renders the error banner + Retry, and renders neither "No collections yet", nor "Unsorted", nor the "no match" panel
  • the fetch is issued with { silent: true } so the shared toast doesn't double-signal
  • clicking Retry recovers the grid and clears the banner
  • a later refresh failure (via the failed-delete recovery path) keeps the already-rendered collections on screen — scoped through the Alpha card with within() rather than indexing a shared-label match list

Full client suite: 833 files / 10238 tests passed, 1 skipped. (One unrelated LoomProductionPanel flake appeared on the first run; it passes in isolation on a clean tree and on a full-suite rerun.)

Closes #6019

https://claude.ai/code/session_01RA3pD5YM2dukQwbZ3pC6WA

#6019)

On /media/collections a rejected listMediaCollections() was swallowed into
`[]`. That made a transient 500 or offline blip render the "No collections
yet" onboarding copy and hand every image and video in the library to the
synthetic "Unsorted" bucket — reading as "all my collections were deleted",
with no error banner and no way to recover once the toast faded.

Collections now use the repo's sentinel convention: `null` = never fetched
or fetch failed, `[]` = the server really has none. A failed read sets an
error state and leaves the list alone (the sentinel on first load, the last
good list on a later refresh), so the grid never claims the library is
unfiled. The page renders a persistent error banner naming the failure with
a Retry button, and the onboarding empty state only appears on a genuine
zero-collection response. The fetch now passes `silent: true` since the page
owns its failure UI.

Claude-Session: https://claude.ai/code/session_01RA3pD5YM2dukQwbZ3pC6WA
@atomantic
atomantic merged commit a0dd8c9 into main Sep 3, 2026
7 checks passed
@atomantic
atomantic deleted the claim/issue-6019 branch September 3, 2026 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant