Skip to content

solid-query 6.0.0-rc.1: a tracked read through createMemo(() => query.data) re-runs on refetch but reads the previous value, and is never notified again #11351

Description

@TylerRick

Describe the bug

A tracked computation that reads query data THROUGH a createMemo(() => query.data) indirection
re-runs after a refetch — but reads the value from before the refetch, and is never notified
again, so its last observation stays stale permanently. At 6.0.0-rc.0 the same computation reads
the new value.

Narrowing, all measured at rc.1:

  • An effect reading query.data directly (no memo) observes the new value — the memo indirection
    is the trigger.
  • The same value change committed synchronously via queryClient.setQueryData() is observed
    through the memo too — the failure needs the refetch path, where the data projection's derive
    returns a promise and the commit is asynchronous.
  • The memo itself never recomputes across the refetch (consistent with the data node being one
    stable store root reconciled in place), and after everything settles the memo's cached value is
    === a direct query.data read — at which point an UNTRACKED read through the memo returns the
    NEW value. So the stale read happens at notify time: the effect re-runs before the new value is
    observable through the memo, and no second notification arrives after it commits.
  • Two pure-solid createProjection mimics of the rc.1 data-node shape (a promise-returning
    derive, a memo over the root leaf, an effect reading through the memo — with and without the
    derive reading its own previous committed value) both pass on solid-js 2.0.0-rc.4, so this does
    not look like a generic solid-js memo-over-projection defect, at least not in the documented
    shape.

A memo over query.data is the natural shape of any wrapper hook that gates or narrows a query's
data before handing it to consumers, so this bites app code that never spells
createMemo(() => query.data) explicitly.

Your minimal, reproducible example

https://github.com/TylerRick/solid-query-stale-read-through-memo-repro

A git repo rather than a sandbox because the repro is a vitest run — four files, pnpm install
and one command.

function Probe() {
	const query = useQuery(() => ({
		queryKey: key,
		queryFn: async () => ({ ...serverValue }),
	}));
	const data = createMemo(() => query.data);
	createEffect(
		() => data()?.video1,
		(video1) => {
			captured = video1;
		},
	);
	return <div />;
}
// serverValue starts { video1: false }; after mount+settle:
// serverValue = { video1: true }; await queryClient.refetchQueries({ queryKey: key }); settle
// rc.0: effect re-runs, captured === true
// rc.1: effect re-runs, captured === false — and no further notification ever arrives

Steps to reproduce

  1. Mount a useQuery, read one of its data properties in a tracked computation THROUGH a
    createMemo(() => query.data).
  2. Let the first fetch settle, then refetchQueries with the queryFn now returning a fresh object
    with a changed value.
  3. The tracked computation re-runs but captures the previous value, and never re-runs again.

Expected behavior

The re-run observes the new value (as at 6.0.0-rc.0), or a second notification follows the commit.
Either way a subscriber's LAST observation should not be the superseded value.

How often does this bug happen?

Every time

Platform

  • OS: Linux (x64)
  • Node: 24.19.0
  • jsdom (vitest) — client-side only, no SSR

Tanstack Query adapter

solid-query

TanStack Query version

@tanstack/solid-query 6.0.0-rc.1, with @tanstack/query-core 5.101.4 (rc.1's own exact
dependency, pinned via a direct dependency and a pnpm.overrides entry, so the query-core bump
that normally rides along with rc.1 is held constant). Clean at @tanstack/solid-query 6.0.0-rc.0
with the same query-core pin.

TypeScript version

n/a — the reproduction's .tsx is transpiled by vitest/vite-plugin-solid and never typechecked.

Additional context

solid-js / @solidjs/web 2.0.0-rc.4, @solidjs/testing-library 1.0.0-beta.2, vitest 3.

Found upgrading an app from rc.0: its wrapper hook returns a gate memo over query.data, so every
fine-grained subscriber downstream of the hook captured pre-refetch data forever after any
refetch. Reduced by subtracting from the failing app test — reconcile: 'id', deep(), the
nested data shape, cache priming, staleTime, and enabled were each removed with the failure
persisting.

Possibly related context: rc.1's rewrite of useBaseQuery onto Solid 2's native async model
(#11308), which replaced the resource machinery with a single createProjection data node.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions