Skip to content

websocket-client>=1.0 floor too low: colab ssh drops the server's 400 reason on websocket-client < 1.6 #139

Description

@crichalchemist

Summary

pyproject.toml declares websocket-client>=1.0 (added in #26), but colab ssh relies on WebSocketBadStatusException.resp_body, which websocket-client only added in 1.6.0. On 1.0–1.5, SSH handshake failures lose the server's explanation.

This is low severity: nothing crashes, and a fresh install resolves a recent websocket-client. It only affects environments that already have an older websocket-client, which pip won't upgrade because >=1.0 is satisfied.

Details

src/colab_cli/commands/ssh.py (_connect_websocket):

except websocket.WebSocketBadStatusException as e:
    status = getattr(e, "status_code", None)
    body = getattr(e, "resp_body", b"") or b""

The getattr fallback prevents a crash, but on websocket-client < 1.6 body is always empty, so a 400 from the runtime comes out as:

[colab] Server rejected pubkey (HTTP 400): . Re-check your key with `ssh-keygen -y -f <key>`.

with the server's reason (e.g. unsupported key type) dropped.

WebSocketBadStatusException.__init__ signature by version (checked in isolated envs):

websocket-client parameters
1.0.0 – 1.5.0 message, status_code, status_message, resp_headers
1.6.0 + message, status_code, status_message, resp_headers, resp_body

Reproduce

Run the suite with every direct dependency at its declared minimum:

uv run --isolated --no-sources --resolution lowest-direct \
  --with 'jupyter-kernel-client==0.9.0' pytest -q tests/
# 3 failed, 348 passed   (websocket-client 1.0.0)

The failures:

  • tests/test_ssh.py::test_ssh_handshake_400_emits_actionable_message[bytes-body] / [str-body]: TypeError constructing the exception with 1.6-style arguments. This one is test-side only.
  • tests/test_ssh_wire_contract.py::test_real_status_mapping_via_loopback[400-...unsupported key type]: assert 'unsupported key type' in '...(HTTP 400): . Re-check your key...'. This is the user-visible regression above.

(jupyter-kernel-client is pinned to 0.9.0 here only because its own declared floor is broken on PyPI; see #137 / #138.)

Adding --with 'websocket-client==1.6.0' to the same command gives 351 passed.

Suggested fix

-    "websocket-client>=1.0",
+    "websocket-client>=1.6",

Like the jkc floor discussed in #138, a raised floor also makes pip install --upgrade move older environments forward, which a satisfied >=1.0 never does.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions