useLiveSuspenseQuery never releases over an on-demand collection whose loadSubset returns a Promise
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 55/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- react, typescript
- Domain
- frontend
Research direction
Start with the useLiveSuspenseQuery implementation and run the supplied @vitest/browser reproduction with npm install && npx vitest run. Compare the synchronous true and Promise-returning loadSubset cases, then verify that an async load suspends once, releases when it resolves, and does not repeatedly recreate the live query.
Written by the indexing model from the issue text.
Description
- I've validated the bug against the latest version of DB packages (
@tanstack/db@0.9.2,@tanstack/react-db@0.4.1)
Describe the bug
useLiveSuspenseQuery over an on-demand collection never leaves its Suspense fallback whenever loadSubset returns a Promise — which is every real asynchronous data source.
LoadSubsetFn is declared (options) => true | Promise<void>. If an implementation answers with the synchronous true, the boundary releases immediately. If it answers with a promise — even one that resolves on the next tick — the component re-suspends without limit.
To Reproduce
Runnable repro, real Chromium via @vitest/browser: https://gist.github.com/MAST1999/c2261d268a68610f18c5a6c45525b468
npm install && npx vitest run
Plain @tanstack/db. No persistence, no Electric, no orderBy, no limit. The only variable between passing and failing is whether loadSubset returns true or a Promise; both deliver the same two rows.
✓ a synchronous loadSubset releases the boundary 54ms
× an async loadSubset resolving after 0ms → text="loading" renders=23525
× an async loadSubset resolving after 50ms → text="loading" renders=416
× an async loadSubset resolving after 500ms → text="loading" renders=56
× an explicit queryKey does not change the outcome
× StrictMode is not the cause
Two things a reviewer would reasonably suspect are ruled out in the repro itself: passing an explicit stable queryKey does not help, and it reproduces with StrictMode removed.
The render count falling as the delay grows (23525 → 416 → 56) is the signature of a re-suspend cycle rather than a busy loop — each retry waits for the promise, so a slower promise fits fewer retries into the same five-second budget.
Expected behavior
An async loadSubset should suspend once and release when it resolves.
Likely mechanism
useLiveSuspenseQuery resets promiseRef/hasBeenReadyRef whenever collectionRef.current !== result.collection and throws observer.preload(). A component that suspends before it has ever mounted does not keep its hook state, so each retry constructs a fresh live query, which is loading, which throws a fresh preload promise, which resolves, which retries. The synchronous-true path never suspends at all, which is why it is the only passing case.
I have not confirmed the React-internals half of that (whether a fiber that suspends pre-mount discards its hook state), so treat the mechanism as a hypothesis and the measurements above as the report.
Additional context
This looks like the general case of two issues I filed earlier today, both of which are ways of arriving at a promise-returning loadSubset:
- #1855 —
orderBy+limitmakes the ordered loader chain subset requests, so the result is never synchronously ready. - #1856 — the SQLite persistence wrapper declares its
loadSubsetasync, so it is a promise even when the wrapped sync answeredtrue.
If this one is fixed, both of those stop being user-visible hangs and become merely redundant work. I'd suggest triaging this first.
- Dominant language
- TypeScript
- Stars
- 3.9k
- Forks
- 266
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 56
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TanStack/db
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Index suggestion for collection size is gated on autoIndex, so it only fires where it is redundant Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
area:tools bug good first issue help wanted priority:P2
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
TaewoooPark/Motifcode#14 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
newrelic-experimental/preflight#793 · 1 comment ·
-
bug 🐞
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
[Bounty proposal] fix(web): memory insights count an evening memory on the next day ($25 proposed) Open
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
BasedHardware/omi#15320 ·