Skip to content

fix(client): clear connectPromise and re-arm reconnect on failed open#5

Closed
grrowl wants to merge 1 commit into
feat/ssrfrom
advisor/002-transport-reconnect-recovery
Closed

fix(client): clear connectPromise and re-arm reconnect on failed open#5
grrowl wants to merge 1 commit into
feat/ssrfrom
advisor/002-transport-reconnect-recovery

Conversation

@grrowl

@grrowl grrowl commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Executes plan 002 from the advisor audit. Fixes a permanent-wedge bug found in the deep audit.

Why

If a reconnect attempt's open() rejects (server unreachable when the retry timer fires), the rejected connectPromise was cached forever — a never-opened socket fires no close event, so the "retries on the following close" path never ran. One transient outage > reconnectDelayMs bricked the transport until page reload.

What

A side .catch on the connect IIFE (src/client/transport.ts): clears the cached rejection and re-arms scheduleReconnect() while subscriptions are live and the close wasn't intentional. Rejection still propagates to demand-path callers (fail-loud preserved). No backoff added — that deferral stands per the file's own comment.

Verification

TDD: both new tests in tests/reconnect-recovery.test.ts demonstrably failed pre-fix (timeout / cached rejection), pass post-fix. reconnect-window.test.ts (the demand-driven-connect invariant) stays green. Reviewer independently audited the double-scheduling risk: coalesced by the existing this.ws / cached-promise guards.

🤖 Generated with Claude Code

A socket that never opens fires no close event, so the close-handler
recovery path could not run. The cached rejected connectPromise persisted
forever, wedging the transport after any transient outage longer than
reconnectDelayMs. Attach a side .catch() to the async IIFE: on rejection,
clear connectPromise so the next connect() starts fresh, and re-arm the
timer while subscriptions are live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@grrowl

grrowl commented Jun 13, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #12, which landed plan-002 on main (adapted for main + published @tanstack/db, validated green) as part of the 0.3.2 bug-fix batch.

Not merging into feat/ssr: feat/ssr will receive this via its rebase onto the new main, so merging here would duplicate/conflict with that. Closing as superseded.

The advisor/* branch is retained (not deleted) until the feat/ssr reconciliation is done — it's the source for restoring any feat/ssr-specific bits (e.g. the readSyncSnapshot test case, the extracted scheduleReconnect method) that main's adapted versions intentionally omit.

@grrowl grrowl closed this Jun 13, 2026
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.

1 participant