Socket.IO "connect_error: websocket error" in CI
When Socket.IO tries to upgrade from polling to a raw WebSocket and the upgrade request is rejected or dropped, engine.io emits connect_error with message "websocket error". Polling may still work, so the symptom is transport-specific.
What this error means
The client fires connect_error with err.message === "websocket error", often only when transports: ["websocket"] forces WebSocket-only and no polling fallback is allowed.
connect_error Error: websocket error
at WS.onError (node_modules/engine.io-client/build/cjs/transport.js:...)
at WebSocket.<anonymous> (node_modules/engine.io-client/build/cjs/transports/websocket.js:...)Common causes
A proxy or gateway does not forward the Upgrade header
A reverse proxy in front of the test server that does not pass Connection: Upgrade and Upgrade: websocket turns the WebSocket handshake into a plain response and the transport errors.
WebSocket-only transport with a broken upgrade path
Forcing transports: ["websocket"] removes the polling fallback, so any upgrade failure becomes a hard connect_error instead of degrading to polling.
How to fix it
Allow polling as a fallback in tests
Keep both transports so a blocked upgrade degrades gracefully instead of failing the connection.
const client = io(url, { transports: ['polling', 'websocket'] });Forward Upgrade headers through any proxy
If a proxy sits in front of the server, configure it to pass the WebSocket upgrade headers.
location /socket.io/ {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}How to prevent it
- Test against the same proxy topology CI uses, not a bare localhost server.
- Keep polling in the transports list unless you specifically test WebSocket-only.
- Verify Upgrade headers survive every hop between client and server.