From 1d1f6c75a9cfcba37211f3ca277cec5207a09863 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 22:59:45 +0000 Subject: [PATCH 1/5] Improve awaitTxId timeout error with debugging info Include the txids received during the timeout period in the TimeoutWaitingForTxIdError message. This helps diagnose the common issue where pg_current_xact_id() is called outside the transaction that performs the mutation. The error now shows: - The txid that was being awaited - Which txids were received during the timeout (or none) - A hint about the common cause and link to documentation --- .../electric-db-collection/src/electric.ts | 22 ++++++++++++++++++- packages/electric-db-collection/src/errors.ts | 17 ++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/electric-db-collection/src/electric.ts b/packages/electric-db-collection/src/electric.ts index 994889ded..6d0f08561 100644 --- a/packages/electric-db-collection/src/electric.ts +++ b/packages/electric-db-collection/src/electric.ts @@ -637,13 +637,33 @@ export function electricCollectionOptions>( if (hasSnapshot) return true return new Promise((resolve, reject) => { + // Track txids at start to know which ones arrived during timeout + const txidsAtStart = new Set(seenTxids.state) + const txidsReceivedDuringTimeout: Array = [] + const timeoutId = setTimeout(() => { unsubscribeSeenTxids() unsubscribeSeenSnapshots() - reject(new TimeoutWaitingForTxIdError(txId, config.id)) + reject( + new TimeoutWaitingForTxIdError( + txId, + config.id, + txidsReceivedDuringTimeout, + ), + ) }, timeout) const unsubscribeSeenTxids = seenTxids.subscribe(() => { + // Track new txids that arrived during the timeout period + for (const seenTxid of seenTxids.state) { + if ( + !txidsAtStart.has(seenTxid) && + !txidsReceivedDuringTimeout.includes(seenTxid) + ) { + txidsReceivedDuringTimeout.push(seenTxid) + } + } + if (seenTxids.state.has(txId)) { debug( `${config.id ? `[${config.id}] ` : ``}awaitTxId found match for txid %o`, diff --git a/packages/electric-db-collection/src/errors.ts b/packages/electric-db-collection/src/errors.ts index 14ee6ab50..d80838ad1 100644 --- a/packages/electric-db-collection/src/errors.ts +++ b/packages/electric-db-collection/src/errors.ts @@ -16,8 +16,21 @@ export class ExpectedNumberInAwaitTxIdError extends ElectricDBCollectionError { } export class TimeoutWaitingForTxIdError extends ElectricDBCollectionError { - constructor(txId: number, collectionId?: string) { - super(`Timeout waiting for txId: ${txId}`, collectionId) + constructor( + txId: number, + collectionId?: string, + receivedTxids?: Array, + ) { + const receivedInfo = + receivedTxids === undefined + ? `` + : receivedTxids.length === 0 + ? `\nNo txids were received during the timeout period.` + : `\nTxids received during timeout: [${receivedTxids.join(`, `)}]` + + const hint = `\n\nThis often happens when pg_current_xact_id() is called outside the transaction that performs the mutation. Make sure to call it INSIDE the same transaction. See: https://electric-sql.com/docs/api/tanstack/troubleshooting#awaittxid-stalls` + + super(`Timeout waiting for txId: ${txId}${receivedInfo}${hint}`, collectionId) this.name = `TimeoutWaitingForTxIdError` } } From b0f7fee6be140d9783946ce55ad9052564f9bb6f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 23:02:10 +0000 Subject: [PATCH 2/5] Fix documentation link in timeout error message --- packages/electric-db-collection/src/errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/electric-db-collection/src/errors.ts b/packages/electric-db-collection/src/errors.ts index d80838ad1..15d7cd5c3 100644 --- a/packages/electric-db-collection/src/errors.ts +++ b/packages/electric-db-collection/src/errors.ts @@ -28,7 +28,7 @@ export class TimeoutWaitingForTxIdError extends ElectricDBCollectionError { ? `\nNo txids were received during the timeout period.` : `\nTxids received during timeout: [${receivedTxids.join(`, `)}]` - const hint = `\n\nThis often happens when pg_current_xact_id() is called outside the transaction that performs the mutation. Make sure to call it INSIDE the same transaction. See: https://electric-sql.com/docs/api/tanstack/troubleshooting#awaittxid-stalls` + const hint = `\n\nThis often happens when pg_current_xact_id() is called outside the transaction that performs the mutation. Make sure to call it INSIDE the same transaction. See: https://tanstack.com/db/latest/docs/collections/electric-collection#common-issue-awaittxid-stalls-or-times-out` super(`Timeout waiting for txId: ${txId}${receivedInfo}${hint}`, collectionId) this.name = `TimeoutWaitingForTxIdError` From 284f461c6f11267a581242a50780672d16da6ae1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:06:35 +0000 Subject: [PATCH 3/5] ci: apply automated fixes --- packages/electric-db-collection/src/errors.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/electric-db-collection/src/errors.ts b/packages/electric-db-collection/src/errors.ts index 15d7cd5c3..a7d8cad74 100644 --- a/packages/electric-db-collection/src/errors.ts +++ b/packages/electric-db-collection/src/errors.ts @@ -30,7 +30,10 @@ export class TimeoutWaitingForTxIdError extends ElectricDBCollectionError { const hint = `\n\nThis often happens when pg_current_xact_id() is called outside the transaction that performs the mutation. Make sure to call it INSIDE the same transaction. See: https://tanstack.com/db/latest/docs/collections/electric-collection#common-issue-awaittxid-stalls-or-times-out` - super(`Timeout waiting for txId: ${txId}${receivedInfo}${hint}`, collectionId) + super( + `Timeout waiting for txId: ${txId}${receivedInfo}${hint}`, + collectionId, + ) this.name = `TimeoutWaitingForTxIdError` } } From 9bfc1a6e82112000f25cea711df4a56d66ca5088 Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Thu, 22 Jan 2026 16:15:36 -0700 Subject: [PATCH 4/5] Refactor: extract nested ternary into helper function Improves readability by replacing nested ternary with explicit conditionals in formatReceivedTxidsInfo helper function. Co-Authored-By: Claude Opus 4.5 --- packages/electric-db-collection/src/errors.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/electric-db-collection/src/errors.ts b/packages/electric-db-collection/src/errors.ts index a7d8cad74..c8491a624 100644 --- a/packages/electric-db-collection/src/errors.ts +++ b/packages/electric-db-collection/src/errors.ts @@ -21,13 +21,7 @@ export class TimeoutWaitingForTxIdError extends ElectricDBCollectionError { collectionId?: string, receivedTxids?: Array, ) { - const receivedInfo = - receivedTxids === undefined - ? `` - : receivedTxids.length === 0 - ? `\nNo txids were received during the timeout period.` - : `\nTxids received during timeout: [${receivedTxids.join(`, `)}]` - + const receivedInfo = formatReceivedTxidsInfo(receivedTxids) const hint = `\n\nThis often happens when pg_current_xact_id() is called outside the transaction that performs the mutation. Make sure to call it INSIDE the same transaction. See: https://tanstack.com/db/latest/docs/collections/electric-collection#common-issue-awaittxid-stalls-or-times-out` super( @@ -38,6 +32,16 @@ export class TimeoutWaitingForTxIdError extends ElectricDBCollectionError { } } +function formatReceivedTxidsInfo(receivedTxids?: Array): string { + if (receivedTxids === undefined) { + return `` + } + if (receivedTxids.length === 0) { + return `\nNo txids were received during the timeout period.` + } + return `\nTxids received during timeout: [${receivedTxids.join(`, `)}]` +} + export class TimeoutWaitingForMatchError extends ElectricDBCollectionError { constructor(collectionId?: string) { super(`Timeout waiting for custom match function`, collectionId) From 2c8c9a1013025ac0daeaf156e79af696c58cd5d5 Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Thu, 22 Jan 2026 16:16:38 -0700 Subject: [PATCH 5/5] Add changeset for txid timeout error improvements Co-Authored-By: Claude Opus 4.5 --- .changeset/improve-txid-timeout-error.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/improve-txid-timeout-error.md diff --git a/.changeset/improve-txid-timeout-error.md b/.changeset/improve-txid-timeout-error.md new file mode 100644 index 000000000..99506bf1a --- /dev/null +++ b/.changeset/improve-txid-timeout-error.md @@ -0,0 +1,5 @@ +--- +'@tanstack/electric-db-collection': patch +--- + +Improve awaitTxId timeout error with debugging info showing which txids were received during the timeout period, plus a hint about the common cause (calling pg_current_xact_id outside the mutation transaction).