Skip to main content
← Back to list
01Issue
BugShippedSwamp CLI
Assigneesstack72

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 client

The 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:

  1. Inspecting ALPN negotiation with openssl s_client — which "confirmed" the theory, because every TLS server picks h2 when the client offers h2,http/1.1. That is normal and is true both of Caddy and of swamp serve itself.
  2. Being one step from setting protocols h1 on the shared :443 listener, 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

  1. Run swamp serve --auth-mode oauth (reachable over TLS; a reverse proxy is not required — reproduces against the pod directly via kubectl port-forward).
  2. swamp auth server-login --server wss://<host> and confirm a command works.
  3. 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-admins from the serve vault and let the server re-register.
  4. Re-run any --server command 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)
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 4 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

7/30/2026, 9:53:58 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack727/30/2026, 8:57:39 PM

Sign in to post a ripple.