github/copilot-sdk · error · RuntimeError

WebSocket forwarding requires the 'websockets' package…

Error message

WebSocket forwarding requires the 'websockets' package. Install it or override open_websocket().

What it means

The WebSocket forwarding path needs the third-party 'websockets' package to establish the upstream connection. It is an optional dependency; when the import fails, open() raises RuntimeError telling the user to install it or override open_websocket() with a custom implementation.

Solutions

  1. Install the optional dependency: pip install 'websockets' (or the package's websocket extra)
  2. Subclass and override open_websocket() to supply your own upstream connection implementation
  3. Pin the dependency in your requirements/Dockerfile so deployment images always include it
  4. Check import websockets in a startup smoke test to fail at boot, not per-request

Example fix

// before
handler = CopilotWebSocketRequestHandler(context)
await handler.open()  # RuntimeError: websockets missing
// after
# pip install websockets
await handler.open()
Defensive patterns

Strategy: fallback

Validate before calling

try:
    import websockets
    HAS_WEBSOCKETS = True
except ImportError:
    HAS_WEBSOCKETS = False

Type guard

def websockets_available() -> bool:
    try:
        import websockets  # noqa: F401
        return True
    except ImportError:
        return False

Try / catch

try:
    await handler.open()
except RuntimeError as e:
    if "websockets" in str(e):
        handler = CustomHandlerWithOwnWebSocket(context)
        await handler.open()
    else:
        raise

Prevention

When it happens

Trigger: Calling open() (directly or via the request lifecycle) on CopilotWebSocketRequestHandler without overriding open_websocket(), in an environment where 'websockets' is not installed.

Common situations: Minimal/production installs that omitted the websocket extra, Docker images with only core deps, or environments that use a different websocket library.

Understand the failure class

Background: "X is not installed. Please install it with pip install Y": missing optional dependency errors — ImportError/ValueError raised when a library's optional extra was never installed — this error's family across 22 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/5b9c00228c7e75fb. Report an issue: GitHub.

Appendix: source

Thrown at python/copilot/copilot_request_handler.py:195

    """Default pass-through WebSocket handler backed by the ``websockets`` library."""

    def __init__(self, context: CopilotRequestContext) -> None:
        super().__init__(context)
        self._upstream: Any | None = None
        self._receive_task: asyncio.Task[None] | None = None

    async def send_request_message(self, data: str | bytes) -> None:
        if self._upstream is None:
            return
        await self._upstream.send(data)

    async def open(self) -> None:
        if self._upstream is not None:
            return
        try:
            import websockets
        except ImportError as exc:  # pragma: no cover - optional dependency
            raise RuntimeError(
                "WebSocket forwarding requires the 'websockets' package. "
                "Install it or override open_websocket()."
            ) from exc

        headers = [
            (name, value)
            for name, values in self.context.headers.items()
            if name.lower() not in _FORBIDDEN_REQUEST_HEADERS
            for value in (values or [])
        ]
        self._upstream = await websockets.connect(self.context.url, additional_headers=headers)
        self._receive_task = asyncio.create_task(self._receive_loop())

    async def _receive_loop(self) -> None:
        try:
            async for message in self._upstream:  # type: ignore[union-attr]
                await self.send_response_message(message)
            await self.close(CopilotWebSocketCloseStatus.normal_closure())

View on GitHub (pinned to cd8cf15dc3)