fix(svelte-query): untrack observer subscription by gzuuus · Pull Request #11542 · TanStack/query · GitHub
Skip to content

fix(svelte-query): untrack observer subscription - #11542

Open
gzuuus wants to merge 1 commit into
TanStack:mainfrom
gzuuus:fix/svelte-query-untrack-subscribe
Open

gzuuus wants to merge 1 commit into
TanStack:mainfrom
gzuuus:fix/svelte-query-untrack-subscribe

Conversation

@gzuuus

@gzuuus gzuuus commented Sep 18, 2026 •

Copy link
Copy Markdown

Fixes #11541

🎯 Changes

For queries without cached data, queryFn executes synchronously inside the subscription $effect (onSubscribe → shouldFetchOnMount → fetch). Any reactive state read by queryFn before its first await was therefore recorded as a dependency of that effect. Writes to that state tore the observer down (cancelling the in-flight fetch) and re-subscribed — and since the query stays dataless, every re-subscription starts a new fetch: an unbounded teardown/refetch loop. See #11541 for the full mechanism and the exact set of conditions that sustain it.

The fix keeps the observer read tracked, but runs the subscription itself inside untrack():

$effect(() => {
  const o = observer
  const unsubscribe = isRestoring.current
    ? () => undefined
    : untrack(() => o.subscribe(() => update(createResult())))
  return unsubscribe
})

Note: the naive untrack(() => observer.subscribe(...)) is not enough — it also untracks the observer read, so changing queries would no longer re-subscribe. The existing test “should track queries added to an initially empty array” catches that case, which is why the observer read stays outside the untrack().

Applied to both createBaseQuery.svelte.ts (also covers createInfiniteQuery) and createQueries.svelte.ts.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes

    • Prevented reactive state updates during an in-flight query from unnecessarily restarting subscriptions or cancelling fetches.
    • Query functions that read state before their first asynchronous wait now complete reliably without repeated refetches.
  • Tests

    • Added regression coverage for single-fetch behavior and successful completion in both individual and multiple-query scenarios.

For queries without cached data, queryFn executes synchronously inside
the subscription effect (onSubscribe -> shouldFetchOnMount -> fetch).
Any reactive state read by queryFn before its first await was therefore
recorded as a dependency of that effect: writes to the state tore the
observer down (cancelling the in-flight fetch) and re-subscribed,
starting a new fetch from a still-dataless query — indefinitely.

Keep the observer read tracked so changing options still re-subscribes,
but run the subscription itself inside untrack().
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

createQuery/createQueries: a queryFn that reads reactive state before its first await causes an infinite observer teardown/refetch loop

1 participant