Problem
When the Supabase backend is unreachable (e.g. a paused project on a preview/free-tier deployment, network failure, or DNS/CORS issue), FestivalSelection shows the "Loading festivals…" spinner forever with no way to recover and no indication anything went wrong.
Discovered on the Vercel preview for #54: the festivals list never appeared. Root cause was a paused Supabase project — the from("festivals").select() request opened and never received a response, so the query promise never settled. Confirmed it was not the #54 refactor (the festivals query is behavior-identical before/after the move).
Why it hangs instead of erroring
src/pages/FestivalSelection.tsx only branches on isLoading:
const { data: availableFestivals = [], isLoading: festivalsLoading } = useFestivalsQuery();
if (festivalsLoading) return <Spinner text="Loading festivals…" />;
- A request that errors flips
isLoading → false, so the page falls through to the empty/list state — handled.
- A request that never settles (unreachable backend, no response) keeps
isLoading === true indefinitely. TanStack Query can't retry a promise that never rejects, so the spinner is permanent.
There is no query timeout and no isError branch, so an unreachable backend is indistinguishable from "still loading."
Proposed fix
- Surface errors in
FestivalSelection — add an isError branch with a readable message ("Couldn't load festivals — retry") and a retry button, instead of only handling isLoading.
- Bound the request so a hung backend eventually fails instead of hanging forever — e.g. an
AbortSignal.timeout(...) passed to the Supabase call in fetchFestivals (and ideally the other top-level fetches), or a query-level timeout, so it transitions to isError and becomes retryable.
- Consider applying the same pattern to the other route loaders/queries that can hang on an unreachable backend (festival-by-slug, editions), since they share the failure mode.
Scope / notes
Acceptance
- An unreachable/paused backend results in a visible error state with retry within a bounded time, not a permanent spinner.
- Normal load and empty ("No Festivals Available") states are unchanged.
Problem
When the Supabase backend is unreachable (e.g. a paused project on a preview/free-tier deployment, network failure, or DNS/CORS issue),
FestivalSelectionshows the "Loading festivals…" spinner forever with no way to recover and no indication anything went wrong.Discovered on the Vercel preview for #54: the festivals list never appeared. Root cause was a paused Supabase project — the
from("festivals").select()request opened and never received a response, so the query promise never settled. Confirmed it was not the #54 refactor (the festivals query is behavior-identical before/after the move).Why it hangs instead of erroring
src/pages/FestivalSelection.tsxonly branches onisLoading:isLoading → false, so the page falls through to the empty/list state — handled.isLoading === trueindefinitely. TanStack Query can't retry a promise that never rejects, so the spinner is permanent.There is no query timeout and no
isErrorbranch, so an unreachable backend is indistinguishable from "still loading."Proposed fix
FestivalSelection— add anisErrorbranch with a readable message ("Couldn't load festivals — retry") and a retry button, instead of only handlingisLoading.AbortSignal.timeout(...)passed to the Supabase call infetchFestivals(and ideally the other top-level fetches), or a query-level timeout, so it transitions toisErrorand becomes retryable.Scope / notes
allfilter → public/admin cache collision (festivals + editions) #55 (festivals cache-key collision).Acceptance