Found while debugging a live playback drop against Music Assistant.
The problem
MainViewModel.cs:1271 rejects a duplicate server-initiated connection with:
_hostService.DisconnectAllAsync("already_connected", ...)
client/goodbye reasons are a closed set in the Sendspin spec (messaging.md:426):
another_server | shutdown | restart | user_request |
unauthorized | pairing_required | concurrent_attempt | unpaired
already_connected is not among them — it appears nowhere in the spec.
Why it matters
A server cannot act on a reason it does not recognise, and the spec's fallback (messaging.md:442) is that a client which disconnects without a usable client/goodbye should be assumed to be restarting and auto-reconnected. So the one message intended to say "stop, I already have a connection to you" reads to a conformant server as "I crashed, please come back" — the opposite of the intent.
Observed live: MA opened a server-initiated connection ~2s after windowsSpin auto-dialled it; the app rejected the duplicate with already_connected; MA then tore down the dial connection too, killing playback 1–2 seconds after it started.
The fix
concurrent_attempt is the spec's reason for exactly this shape — another connection or attempt is already in progress with this client.
The SDK now exposes Sendspin.SDK.Protocol.Messages.GoodbyeReasons (see sendspin-dotnet#136) with a constant per spec reason, so use GoodbyeReasons.ConcurrentAttempt rather than a literal.
Related
- sendspin-dotnet#136 fixed the same class of bug on the SDK side: three non-spec reasons (
disposing, handshake_timeout, handshake_failed) plus a swallowed ObjectDisposedException that meant no goodbye was ever sent on dispose.
- The
App.OnExit fix (currently uncommitted in the working tree) is what makes any goodbye reach the server at all — as async void, WPF exited the process at the first await.
Note
Once the SDK fix and the OnExit fix are both in, MA should stop auto-reconnecting to an exited client, so the duplicate-connection collision should become rare. This is still worth fixing: it is the wrong signal whenever the collision does happen for another reason.
Found while debugging a live playback drop against Music Assistant.
The problem
MainViewModel.cs:1271rejects a duplicate server-initiated connection with:client/goodbyereasons are a closed set in the Sendspin spec (messaging.md:426):already_connectedis not among them — it appears nowhere in the spec.Why it matters
A server cannot act on a reason it does not recognise, and the spec's fallback (
messaging.md:442) is that a client which disconnects without a usableclient/goodbyeshould be assumed to be restarting and auto-reconnected. So the one message intended to say "stop, I already have a connection to you" reads to a conformant server as "I crashed, please come back" — the opposite of the intent.Observed live: MA opened a server-initiated connection ~2s after windowsSpin auto-dialled it; the app rejected the duplicate with
already_connected; MA then tore down the dial connection too, killing playback 1–2 seconds after it started.The fix
concurrent_attemptis the spec's reason for exactly this shape — another connection or attempt is already in progress with this client.The SDK now exposes
Sendspin.SDK.Protocol.Messages.GoodbyeReasons(see sendspin-dotnet#136) with a constant per spec reason, so useGoodbyeReasons.ConcurrentAttemptrather than a literal.Related
disposing,handshake_timeout,handshake_failed) plus a swallowedObjectDisposedExceptionthat meant no goodbye was ever sent on dispose.App.OnExitfix (currently uncommitted in the working tree) is what makes any goodbye reach the server at all — asasync void, WPF exited the process at the firstawait.Note
Once the SDK fix and the
OnExitfix are both in, MA should stop auto-reconnecting to an exited client, so the duplicate-connection collision should become rare. This is still worth fixing: it is the wrong signal whenever the collision does happen for another reason.