Repository navigation
Fix stuck WebSocket reconnect and connection ownership - #4
Merged
dezinezync merged 1 commit intoOct 13, 2026
Merged
Conversation
A failed retry was ignored while the client was reconnecting, so the client stayed in .reconnecting forever. Callbacks, the retry timer, and the receive and ping loops also had no link to the connection that started them, so events from an old connection could affect a new one. - Schedule the next retry when a retry fails, up to the policy limit - Store the retry timer so connect() and disconnect() can cancel it - Ignore connect() while a retry handshake is in progress - Pass the originating task to every callback and ignore old ones - Count close and completion for the same failure once - Stop ping and receive loops as soon as a connection drops - Skip the state-change Task when no handler is set Public API, policy calculations, error reporting and send behavior are unchanged. All existing tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a reconnect attempt failed,
handleDisconnection()returned early, because its guard only accepted.connectedor.connectingand a retry runs in.reconnecting. Nothing scheduled the next attempt, so the client stayed in.reconnectingforever and anysendwaiting for the connection never resumed.Callbacks, the retry timer, and the receive and ping loops also had no link to the connection that started them, so events from an old connection could affect a new one.
Changes
All changes are in
DZWebSocketClient.swift.connect()anddisconnect()can cancel it. An old timer can no longer start an attempt in a later cycle.connect()does nothing while a retry handshake is in progress. During the wait between retries, it retries right away without resetting the attempt count.onErrorstill reports every error.onStateChangehandler is set.Compatibility
swift package diagnose-api-breaking-changes mainreports none.ReconnectionPolicyis unchanged.disconnect()is called. PassingReconnectionPolicy()gives a finite backoff.Testing
swift teston macOS and on the iOS 26.5 simulator.mainand this branch using local test servers. These scenarios fail onmainand pass with this change:connect()calls start no extra handshakesKnown issues, for a follow-up PR
testTaskCancellationis flaky onmaintoo (about 1 in 10 runs). Insend(message:responseType:), the cancel Task can reach the actor before the request is registered, so a cancelled request can still return successfully.sendreturns without sending, and a pending request can wait indefinitely.🤖 Generated with Claude Code