Relationships
#1482 CLI reports 'HTTP/2 not supported by this client' when the server actually returned 401 — masks auth failures as a transport error
Opened by keeb · 7/30/2026· Shipped 7/30/2026
Summary
When a --server connection is rejected for authentication reasons, the CLI reports it as an HTTP/2 transport incompatibility:
Error: Could not connect to wss://[HOST-1]/: NetworkError: failed to connect to WebSocket: HTTP/2 not supported by this clientThe server is not speaking HTTP/2 to a client that cannot handle it. The server returned 401. The message names the wrong subsystem entirely, and points the user at their reverse proxy / TLS stack instead of at swamp auth server-login.
Impact
This actively misdirects debugging. Diagnosing a live swamp serve behind Caddy, the error led to:
- Inspecting ALPN negotiation with
openssl s_client— which "confirmed" the theory, because every TLS server picksh2when the client offersh2,http/1.1. That is normal and is true both of Caddy and ofswamp serveitself. - Being one step from setting
protocols h1on the shared:443listener, which would have disabled HTTP/2 for every unrelated site on that host and fixed nothing.
The error is most damaging exactly when it appears, because ALPN evidence gathered afterward is consistent with the false explanation. Related: #890 (server-side redeem hiding the real error at debug level) — same class of problem, opposite end of the wire.
Reproduction
- Run
swamp serve --auth-mode oauth(reachable over TLS; a reverse proxy is not required — reproduces against the pod directly viakubectl port-forward). swamp auth server-login --server wss://<host>and confirm a command works.- Invalidate the stored credential server-side. Any of these does it:
- switch the datastore to a fresh namespace (session/token state lives in the datastore tier), or
- delete
oauth-client-id/oauth-client-secret/oauth-resolved-adminsfrom the serve vault and let the server re-register.
- Re-run any
--servercommand with the now-stale credential.
Expected: an authentication error naming the credential, e.g. Authentication failed - run: swamp auth server-login --server wss://<host>.
Actual: NetworkError: failed to connect to WebSocket: HTTP/2 not supported by this client.
The server log makes the true cause unambiguous at INFO/WARN level:
[WRN] serve: WebSocket auth rejected from "[IP-1]": "Authentication failed"So the information exists on the server side at the same moment — it is only the client-side error mapping that is wrong.
Suggested fix
Distinguish a WebSocket handshake that failed with an HTTP 401/403 status from a genuine ALPN/protocol mismatch, and surface the former as an auth error with the remediation command. If the underlying WS client cannot expose the handshake status, a plain-HTTPS probe of GET / (unauthenticated health endpoint, already present) on connect failure would disambiguate: reachable + healthy ⇒ the problem is credentials, not transport.
Worth auditing whether the same generic Could not connect to wss://... wrapper masks other distinct causes — an untrusted TLS cert (DENO_CERT not set) presents through the same path, so users cannot tell a cert problem from a 401 from a real protocol mismatch.
Environment
- Client:
swamp 20260206.200442.0-sha.(Linux) - Server:
swamp serve 20260721.232417.0-sha.7fd0d628,--auth-mode oauth, TLS on, behind Caddy 2 (also reproduces with the proxy removed)
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.