Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

@KyleAMathews KyleAMathews commented Jan 22, 2026

Summary

Fix unhandled promise rejections when collections are cleaned up while requestSnapshot or fetchSnapshot calls are still in-flight. Adds comprehensive test coverage for the error suppression behavior.


Reviewer Guidance

Root Cause

When a collection is cleaned up, the abort controller signals cancellation. However, any in-flight requestSnapshot or fetchSnapshot calls would reject (often with 409 "must-refetch" errors), and these rejections were unhandled—causing console warnings and potential issues in error-sensitive environments.

Approach

  1. Pass abort signal to createLoadSubsetDedupe: The abort signal is now available in the subset loading logic
  2. Wrap snapshot calls in try-catch: Both fetchSnapshot (progressive mode) and requestSnapshot (on-demand mode) are now wrapped
  3. Check signal.aborted before re-throwing: If the signal is aborted, errors are logged at debug level and suppressed; otherwise, they propagate normally
  4. Extract helper function: The duplicate error handling logic is consolidated into handleSnapshotError

Key Invariants

  • Errors during cleanup (when signal.aborted === true) are silently ignored
  • Errors during normal operation (when signal.aborted === false) are re-thrown
  • The mock AbortController in tests now actually aborts the signal when abort() is called

Non-goals

  • Did not implement error type discrimination (checking for specific 409 status codes)—the current approach catches all errors during abort, which is acceptable for cleanup scenarios
  • Did not add production-level logging for suppressed errors (uses debug logging only)

Trade-offs

The silent-failure-hunter agent identified that catching ALL errors when aborted (not just abort-related ones) could theoretically hide unrelated errors that happen to occur during cleanup. However:

  • The window for this race condition is very small
  • Cleanup is a terminal operation where error recovery isn't meaningful anyway
  • Adding error type discrimination would add complexity for minimal benefit

Verification

# Run the new tests specifically
pnpm vitest run -t "should ignore requestSnapshot errors"
pnpm vitest run -t "should ignore fetchSnapshot errors"
pnpm vitest run -t "should re-throw"

# Run all lifecycle tests
pnpm vitest run -t "Electric stream lifecycle management"

# Run full test suite
pnpm test

Files Changed

File Changes
src/electric.ts Extract handleSnapshotError helper, refactor duplicate catch blocks
tests/electric.test.ts Fix mock AbortController, add 4 tests for error suppression behavior

Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

Release Impact

  • This change affects published code, and I have generated a changeset.

🤖 Generated with Claude Code

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

⚠️ No Changeset found

Latest commit: 8e2a877

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 22, 2026

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/offline-transactions

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

@tanstack/powersync-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 8e2a877

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

Size Change: 0 B

Total Size: 90.9 kB

ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.19 kB
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.32 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.68 kB
./packages/db/dist/esm/collection/mutations.js 2.34 kB
./packages/db/dist/esm/collection/state.js 3.49 kB
./packages/db/dist/esm/collection/subscription.js 3.62 kB
./packages/db/dist/esm/collection/sync.js 2.41 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.7 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.69 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.93 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 4.08 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.42 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.81 kB
./packages/db/dist/esm/query/compiler/index.js 2.02 kB
./packages/db/dist/esm/query/compiler/joins.js 2.07 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.45 kB
./packages/db/dist/esm/query/compiler/select.js 1.06 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.42 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.93 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 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.3 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 924 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/cursor.js 457 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 Jan 22, 2026

Size Change: 0 B

Total Size: 3.7 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.34 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 559 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

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

@K-Mistele
Copy link

any ETA on this? would really help stabilize our CI checks :)

When a collection is cleaned up while requestSnapshot or fetchSnapshot
calls are still in-flight, the abort signal causes these calls to fail
with HTTP 409 "must-refetch" errors. These errors were propagating as
unhandled promise rejections because they bypassed the onError handler
(which only catches errors from the ShapeStream subscription).

This fix:
- Passes the abort signal to createLoadSubsetDedupe
- Wraps requestSnapshot and fetchSnapshot calls in try-catch blocks
- Silently ignores errors when the signal is aborted (during cleanup)
- Re-throws errors if the signal is not aborted (real errors)
@KyleAMathews KyleAMathews force-pushed the claude/investigate-409-errors-RuA1x branch from c080dc6 to ba076b7 Compare January 23, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for review

Development

Successfully merging this pull request may close these issues.

4 participants