You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I've validated the bug against the latest version of DB packages (@tanstack/db@0.6.17)
Describe the bug
A materialize() correlated subquery silently resolves to an empty array when its correlation
predicate references a joined alias instead of the subquery's own from alias.
// ❌ always [] — correlation reaches `part.id` through the joined `order` aliasmaterialize(q.from({production: productions}).innerJoin({order: orders},({ production, order })=>eq(production.orderId,order.id)).where(({ order })=>eq(order.partId,part.id)))// ✅ works — correlation targets the subquery's own `from` aliasmaterialize(q.from({order: orders}).where(({ order })=>eq(order.partId,part.id)))
There is no error or warning — the field is just permanently empty, so the failure is silent data
loss. In our app this quietly dropped two whole relations out of a stock ledger, and it went
unnoticed because the surrounding relations populated correctly.
To Reproduce
npm i @tanstack/db@0.6.17
Save as repro.cjs:
const{
createCollection, createLiveQueryCollection, eq, localOnlyCollectionOptions, materialize,}=require("@tanstack/db");constmk=(id,initialData)=>createCollection(localOnlyCollectionOptions({ id,getKey: (r)=>r.id, initialData }));constparts=mk("part",[{id: 1}]);constorders=mk("order",[{id: 1,partId: 1}]);constproductions=mk("production",[{id: 1,orderId: 1}]);constlive=createLiveQueryCollection({startSync: true,query: (q)=>q.from({part: parts}).select(({ part })=>({
...part,// CONTROL — correlation references this subquery's own `from` alias.control: materialize(q.from({order: orders}).where(({ order })=>eq(order.partId,part.id)),),// BUG — identical correlation, but reached through a joined alias.broken: materialize(q.from({production: productions}).innerJoin({order: orders},({ production, order })=>eq(production.orderId,order.id)).where(({ order })=>eq(order.partId,part.id)).select(({ production })=>({ ...production})),),})).findOne(),});setTimeout(()=>{constrow=Array.from(live.entries(),([,v])=>v)[0];console.log("control (expect 1):",row?.control?.length??0);console.log("broken (expect 1):",row?.broken?.length??0);},100);
node repro.cjs
control (expect 1): 1
broken (expect 1): 0
Expected behavior
broken should contain the single production row, the same as running that join + where
outside materialize().
Additional context
The join and predicate are correct in isolation. The exact same from(production).innerJoin(order).where(order.partId === 1) as a plain, uncorrelated live query
returns 1 row. Only the correlated materialize() form returns [].
Not an indexing artifact. Adding the indexes suggested by the "Join requires an index on \"orderId\"" warning (production.orderId, order.partId, order.id, with defaultIndexType: BasicIndex) produces the identical broken: 0.
Not a sync-timing artifact. All rows are present before the query is built, localOnly
collections, and the result is stable indefinitely — it never converges.
Reproduced first against Electric-backed collections in a real app, then reduced to the localOnly case above.
Possibly related to the lazy-target/alias resolution that RFC RFC: Stabilizing includes / nested materialization #1658 (PR 2) restructures — the
correlation target here is resolved through a joined alias — but this failure mode isn't among
the issues that RFC enumerates.
@tanstack/db@0.6.17)Describe the bug
A
materialize()correlated subquery silently resolves to an empty array when its correlationpredicate references a joined alias instead of the subquery's own
fromalias.There is no error or warning — the field is just permanently empty, so the failure is silent data
loss. In our app this quietly dropped two whole relations out of a stock ledger, and it went
unnoticed because the surrounding relations populated correctly.
To Reproduce
npm i @tanstack/db@0.6.17repro.cjs:node repro.cjsExpected behavior
brokenshould contain the singleproductionrow, the same as running that join +whereoutside
materialize().Additional context
from(production).innerJoin(order).where(order.partId === 1)as a plain, uncorrelated live queryreturns 1 row. Only the correlated
materialize()form returns[]."Join requires an index on \"orderId\""warning (production.orderId,order.partId,order.id, withdefaultIndexType: BasicIndex) produces the identicalbroken: 0.localOnlycollections, and the result is stable indefinitely — it never converges.
localOnlycase above.correlation target here is resolved through a joined alias — but this failure mode isn't among
the issues that RFC enumerates.
Environment
@tanstack/db@0.6.17(current latest), Node v24.14.0