Area
packages/client-runtime (connections), surfaced in apps/web Settings → Connections
Summary
If a v2 client disables a saved direct connection because the host was still on v1, the connection stays disabled after the host is updated to v2 some other way. The switch is disabled ("Client not supported"). The only action left is Update, which fails against a host that is already on v2.
Steps to reproduce
- Save a direct (non-relay) connection from a Nightly desktop client to a remote host, here a Windows desktop reached over a Tailscale IP.
- Update the client to a v2 Nightly while the host is still on v1. The client marks the environment unsupported (
serverUpdateRequired) and switches it off.
- Update the host to the same v2 Nightly yourself on that machine, not through the client's Update button.
- On the client, open Settings → Connections.
Expected behavior
The client sees that the host now speaks orchestration protocol 2, clears the block and lets the connection be switched back on. Pressing Update when the host is already compatible should do the same.
Actual behavior
- The switch stays disabled, and nothing clears the block.
unsupportedReason is only cleared by relay discovery (connection/layer.ts, relay targets only) or by a successful updateOutdatedHost. A disabled direct connection never runs a socket handshake, so it never checks again.
- Pressing Update calls
updateOutdatedHost. That gets its socket URL from resolver.prepareForUpdate (= authorize), which does not call appendOrchestrationProtocol, and it never checks whether the descriptor it just fetched is already compatible. The v2 host rejects the bare socket with 426 orchestration_protocol_incompatible, so the update fails.
- The UI shows
Client not supported and SocketOpenError: An error occurred during Open during update
Impact
Blocks work completely for that connection until the client restarts. It will likely affect anyone who updates their machines one at a time during the move to V2 (#14871).
Version or commit
Nightly 0.0.46-nightly.20261003.2638 (5e35272) on both sides. packages/client-runtime/src/connection/ has not changed on main since then.
Environment
Client: macOS desktop Nightly. Host: Windows 11 desktop Nightly (NT 10.0.26200, x64), direct connection over Tailscale.
Logs
Host server.trace.ndjson, requests from the macOS client (user agent T3Code(Nightly)/0.0.46-nightly.20261003.2638). The descriptor and ticket requests succeed, then the socket is rejected:
http.server GET /.well-known/t3/environment status=200
http.server POST /api/auth/websocket-ticket status=200
http.server GET /ws status=426
apps/server/src/ws.ts returns 426 only when the orchestrationProtocol query param is missing or wrong.
Possible fix
In updateOutdatedHost, if the descriptor from prepareForUpdate already passes isCompatibleDescriptor, skip the self-update RPCs and go straight to the resume step: clear compatibility and re-enable.
Workaround
Confirmed: restart the client, then switch the connection back on in Settings → Connections. This works because unsupportedReason is held in memory and only enabled: false is persisted.
Investigated with Claude Code (Claude Opus 5.5).
Area
packages/client-runtime (connections), surfaced in apps/web Settings → Connections
Summary
If a v2 client disables a saved direct connection because the host was still on v1, the connection stays disabled after the host is updated to v2 some other way. The switch is disabled ("Client not supported"). The only action left is Update, which fails against a host that is already on v2.
Steps to reproduce
serverUpdateRequired) and switches it off.Expected behavior
The client sees that the host now speaks orchestration protocol 2, clears the block and lets the connection be switched back on. Pressing Update when the host is already compatible should do the same.
Actual behavior
unsupportedReasonis only cleared by relay discovery (connection/layer.ts, relay targets only) or by a successfulupdateOutdatedHost. A disabled direct connection never runs a socket handshake, so it never checks again.updateOutdatedHost. That gets its socket URL fromresolver.prepareForUpdate(=authorize), which does not callappendOrchestrationProtocol, and it never checks whether the descriptor it just fetched is already compatible. The v2 host rejects the bare socket with426 orchestration_protocol_incompatible, so the update fails.Client not supportedandSocketOpenError: An error occurred during Openduring updateImpact
Blocks work completely for that connection until the client restarts. It will likely affect anyone who updates their machines one at a time during the move to V2 (#14871).
Version or commit
Nightly 0.0.46-nightly.20261003.2638 (5e35272) on both sides.
packages/client-runtime/src/connection/has not changed onmainsince then.Environment
Client: macOS desktop Nightly. Host: Windows 11 desktop Nightly (NT 10.0.26200, x64), direct connection over Tailscale.
Logs
Host
server.trace.ndjson, requests from the macOS client (user agentT3Code(Nightly)/0.0.46-nightly.20261003.2638). The descriptor and ticket requests succeed, then the socket is rejected:apps/server/src/ws.tsreturns 426 only when theorchestrationProtocolquery param is missing or wrong.Possible fix
In
updateOutdatedHost, if the descriptor fromprepareForUpdatealready passesisCompatibleDescriptor, skip the self-update RPCs and go straight to the resume step: clear compatibility and re-enable.Workaround
Confirmed: restart the client, then switch the connection back on in Settings → Connections. This works because
unsupportedReasonis held in memory and onlyenabled: falseis persisted.Investigated with Claude Code (Claude Opus 5.5).