Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

@KyleAMathews KyleAMathews commented Nov 26, 2025

Fixed SyncNotInitializedError being thrown when calling write operations (writeUpsert, writeInsert, etc.) or mutations (insert, update, delete) on collections before sync is started. Previously, these operations required startSync: true to be explicitly set or preload() to be called first. Now, sync is automatically started when any write operation or mutation is called on an idle collection, enabling these operations to work immediately without explicit initialization.

Fixes the issue where users with on-demand collections could not manually insert related records using writeUpsert.

…ions

On-demand collections were throwing SyncNotInitializedError when users
tried to call writeUpsert or other write operations. This happened because
the sync function was only invoked when startSync: true was explicitly set,
but write operations require the sync infrastructure to be initialized.

For on-demand collections, starting sync is cheap (no data is fetched until
loadSubset is called), so we now automatically start sync when syncMode is
'on-demand'. This ensures write operations work immediately while maintaining
the on-demand loading behavior.

Fixes the issue where users with on-demand collections could not manually
insert related records using writeUpsert.
@changeset-bot
Copy link

changeset-bot bot commented Nov 26, 2025

🦋 Changeset detected

Latest commit: 13c7314

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/db Patch
@tanstack/query-db-collection Patch
@tanstack/angular-db Patch
@tanstack/db-collection-e2e Patch
@tanstack/electric-db-collection Patch
@tanstack/powersync-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 26, 2025

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@918

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@918

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@918

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@918

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@918

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@918

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@918

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@918

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@918

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@918

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@918

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@918

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@918

commit: 13c7314

@github-actions
Copy link
Contributor

github-actions bot commented Nov 26, 2025

Size Change: +49 B (+0.06%)

Total Size: 87.2 kB

Filename Size Change
./packages/db/dist/esm/collection/index.js 3.28 kB +43 B (+1.33%)
./packages/db/dist/esm/collection/lifecycle.js 1.67 kB +6 B (+0.36%)
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 977 B
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/mutations.js 2.31 kB
./packages/db/dist/esm/collection/state.js 3.43 kB
./packages/db/dist/esm/collection/subscription.js 2.55 kB
./packages/db/dist/esm/collection/sync.js 2.37 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.19 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.64 kB
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 1.87 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 513 B
./packages/db/dist/esm/local-only.js 837 B
./packages/db/dist/esm/local-storage.js 2.1 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.75 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/index.js 3.96 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 917 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.35 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.8 kB
./packages/db/dist/esm/query/compiler/index.js 1.96 kB
./packages/db/dist/esm/query/compiler/joins.js 2 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.25 kB
./packages/db/dist/esm/query/compiler/select.js 1.07 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.33 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.74 kB
./packages/db/dist/esm/query/live/internal.js 130 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.91 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.3 kB
./packages/db/dist/esm/SortedMap.js 1.18 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils.js 881 B
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 852 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Nov 26, 2025

Size Change: 0 B

Total Size: 3.35 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.12 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 431 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

On-demand collections now start in 'ready' status immediately since sync
is auto-started to enable write operations. Updated the test to expect
'ready' instead of 'idle' initial status while still verifying no data
is loaded until loadSubset is called.
Instead of only auto-starting sync for on-demand collections, we now
auto-start sync whenever any write operation is called on a collection
that hasn't been initialized yet. This provides a more general solution
that works for both eager and on-demand collections.

Changes:
- Modified QueryCollectionUtilsImpl to store a collection reference
- Write methods now call _ensureSyncStarted() before executing
- Collection passes its reference to utils via _setCollection()
- Updated tests to verify sync auto-starts on first write operation
…ctions

Extended the auto-start sync behavior to cover both mutations and write
operations:

1. Mutations (insert, update, delete) - handled by adding 'idle' to the
   validateCollectionUsable() switch case in lifecycle.ts

2. Write operations (writeInsert, writeUpsert, etc.) - handled by passing
   the collection reference to utils via _setCollection() so they can call
   startSyncImmediate() when needed

This ensures both types of write operations work on collections that
haven't been explicitly preloaded, fixing SyncNotInitializedError.
Instead of passing collection reference via _setCollection, make utils
a getter that calls validateCollectionUsable() before returning.
This auto-starts sync when any util method is accessed, without needing
special internal APIs.

Changes:
- Made utils a getter/setter in CollectionImpl that validates on access
- Removed _setCollection and _ensureSyncStarted from QueryCollectionUtilsImpl
- Updated test to reflect new auto-start behavior when accessing utils
@KyleAMathews KyleAMathews changed the title Automatically start sync for on-demand collections Automatically start sync when there's any mutations Dec 3, 2025
Copy link
Collaborator

@samwillis samwillis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't fix the issue. It only works on query collections with direct writes.

Comment on lines -213 to +223
// Utilities namespace
// This is populated by createCollection
public utils: Record<string, Fn> = {}
// Utilities namespace - stored privately, accessed via getter that validates collection state
private _utils: Record<string, Fn> = {}

public get utils(): Record<string, Fn> {
this._lifecycle.validateCollectionUsable(`utils`)
return this._utils
}

public set utils(value: Record<string, Fn>) {
this._utils = value
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very strange way to implement this. It seems to be a hack that means that when you access the collection utils at all it will call _lifecycle.validateCollectionUsable and trigger sync to start?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah — it seems like a reasonable approach to me — the other way was to have query collection manually call startSync but the idea here is that if a collection is active in any way to start sync. Which covers a lot of stuff.

Copy link
Collaborator

@samwillis samwillis Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this doesn't fix collection.insert or using a custom mutation (are they also broken?), and will trigger sync to start for any collection implementation that places anything it want on the utils, some of which could very much be intended not to start sync.

I really don't think this is the right approach.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

say a sync engine added a utils.status, accessing that would trigger sync to start.

expect(collection.get(`1`)).toEqual(newItem)

// Test writeUpsert (the specific operation from the bug report)
collection.utils.writeUpsert({ id: `2`, name: `Item 2`, value: 20 })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the issue was with any mutation, not just the direct writes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

4 participants