…s queries still fetch
fetchOptimistic builds the query from the observer's options but then calls query.fetch() without them. QueryCache.build returns an existing query without applying options, so a query that was rebuilt without a queryFn - by broadcastQueryClient from a cross-tab message, or hydrated - throws Missing queryFn when a suspending observer fetches it before any observer has attached: the fallback at the top of fetch() only looks at already-attached observers, and there are none yet in this path.
Reported through query-broadcast-client-experimental: when a query is removed and re-added cross-tab, the rebuilt query stays without a queryFn if the other tab's fetch never succeeds, and a mounted useSuspenseQuery crashes with Missing queryFn on its next optimistic fetch instead of fetching with its own options.
The regression test reproduces that sequence: observer bound -> query removed -> rebuilt from key+hash only -> fetchOptimistic. It fails with Missing queryFn before this change and succeeds after. Full queryObserver suite (76) and broadcast client suite (13) pass.
Fixes TanStack#12129
On the server, a query or mutation with an explicit finite
gcTimeschedules a realsetTimeoutduring SSR: the server default ofInfinityis applied throughupdateGcTime's fallback, which an explicit value bypasses. The timer serves no purpose - a request-scopedQueryClientis discarded together with the response - but a pending timer keeps the render's async context (AsyncLocalStorage) alive until it fires, retaining the HTML, the RSC payload, and theRequestheld byreact-dom/server.Reported in #12025 with a real-world case: a single
useQuery({ gcTime: 24h })in a Next.js root provider left a 24-hour timer and ~1.4 MB of retained memory behind on every render; an idle replica accumulated 272Requestobjects and 268 pending timers before being OOM-killed.scheduleGcnow returns early in server environments, so no gc timer is scheduled for anygcTimevalue - the same behavior as the server default. Client behavior is unchanged (the server check is the existingenvironmentManagerdetection, already used for thegcTimefallback and retry defaults).Regression test: with the server environment forced, a finished query with
gcTime: 1000must leavevi.getTimerCount()at 0 and still be in the cache after advancing past the gcTime. Verified to fail before (expected 1 to be +0) and pass after. Full query + mutation suites pass (81/81).Fixes #12025
Summary by CodeRabbit