-
Notifications
You must be signed in to change notification settings - Fork 913
Description
Describe the bug
After running several automation actions, the Steel UI intermittently becomes blank and stays on the banner "Session connecting...". Sessions list API responds 200, but the UI cannot attach to the session. Docker logs show a CDP WebSocket proxy failure to 127.0.0.1:9222 and a separate WebSocket "socket hang up" to ws://0.0.0.0:3000/. The state does not recover without restarting containers.
To Reproduce
- Self-host Steel via Docker (API on 3000/9223, UI on 5173) per docs.
- Open the UI, create a session, and run some automation (navigate, open devtools/cast, etc.).
- After a few minutes/actions, return to the UI session view.
- The canvas is blank and the header shows "Session connecting..." indefinitely.
I used the standard Docker setup referenced in docs:
- Ports: API 3000; debugger 9223; UI 5173. See: Steel docs – Docker.
- Sessions/CDP overview: Steel docs – Sessions & CDP.
Expected behavior
- The UI attaches to the running session and renders the page stream.
- If the session has ended, the UI shows an actionable error with guidance, not an endless "connecting" state.
Screenshots
- Attached: blank UI showing "Session connecting..." and DevTools console with repeated websocket messages and sandbox warnings.

Logs
Docker API logs at the time of failure:
{"level":30,"time":1761790992016,"pid":1,"hostname":"13765d0bbc87","reqId":"req-hz","ip":"192.168.215.1","url":"/v1/sessions","method":"GET","statusCode":200,"durationMs":0.65,"msg":"request completed"}
{"level":30,"time":1761790993027,"pid":1,"hostname":"13765d0bbc87","reqId":"req-i0","ip":"192.168.215.1","url":"/v1/sessions","method":"GET","statusCode":200,"durationMs":1.64,"msg":"request completed"}
{"level":30,"time":1761790994142,"pid":1,"hostname":"13765d0bbc87","reqId":"req-i1","ip":"192.168.215.1","url":"/v1/sessions","method":"GET","statusCode":200,"durationMs":3.66,"msg":"request completed"}
{"level":30,"time":1761790995161,"pid":1,"hostname":"13765d0bbc87","reqId":"req-i2","ip":"192.168.215.1","url":"/v1/sessions","method":"GET","statusCode":200,"durationMs":0.64,"msg":"request completed"}
{"level":30,"time":1761791121650,"pid":1,"hostname":"13765d0bbc87","msg":"Upgrading browser socket..."}
{"level":30,"time":1761791121751,"pid":1,"hostname":"13765d0bbc87","msg":"Connecting to cast..."}
{"level":30,"time":1761791121837,"pid":1,"hostname":"13765d0bbc87","reqId":"req-i3","ip":"192.168.215.1","url":"/v1/sessions","method":"GET","statusCode":200,"durationMs":2.02,"msg":"request completed"}
{"level":30,"time":1761791121847,"pid":1,"hostname":"13765d0bbc87","msg":"Upgrading browser socket..."}
{"level":30,"time":1761791121847,"pid":1,"hostname":"13765d0bbc87","msg":"Connecting to CDP..."}
{"level":50,"time":1761791121856,"pid":1,"hostname":"13765d0bbc87","component":"CDPService","msg":"WebSocket proxy error: Error: connect ECONNREFUSED 127.0.0.1:9222"}
{"level":30,"time":1761791121856,"pid":1,"hostname":"13765d0bbc87","component":"CDPService","msg":"[CDPService] WebSocket connection listeners cleaned up"}
Error in cast session: ErrorEvent {
[Symbol(kTarget)]: WebSocket {
_events: [Object: null prototype] { open: [Function], error: [Function] },
_eventsCount: 2,
_maxListeners: undefined,
_binaryType: 'nodebuffer',
_closeCode: 1006,
_closeFrameReceived: false,
_closeFrameSent: false,
_closeMessage: <Buffer >,
_closeTimer: null,
_errorEmitted: true,
_extensions: {},
_paused: false,
_protocol: '',
_readyState: 3,
_receiver: null,
_sender: null,
_socket: null,
_bufferedAmount: 0,
_isServer: false,
_redirects: 0,
_autoPong: true,
_url: 'ws://0.0.0.0:3000/',
_originalIpc: false,
_originalSecure: false,
_originalHostOrSocketPath: '0.0.0.0:3000',
_req: null,
[Symbol(shapeMode)]: false,
[Symbol(kCapture)]: false
},
[Symbol(kType)]: 'error',
[Symbol(kError)]: Error: socket hang up
at Socket.socketOnEnd (node:_http_client:542:25)
at Socket.emit (node:events:536:35)
at endReadableNT (node:internal/streams/readable:1698:12)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
code: 'ECONNRESET'
},
[Symbol(kMessage)]: 'socket hang up'
}
Browser DevTools console (from the UI tab) also shows:
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.Error while parsing the 'sandbox' attribute: 'allow-clipboard-write', 'allow-clipboard-read' are invalid sandbox flags.- WebSocket connection opened/closed repeatedly for tab discovery.
Desktop (please complete the following information):
- OS: macOS M1 2020 (Darwin OS Tahoe 26.0.1)
- Browser: Chrome (latest stable at time of report)
- Docker Desktop: latest
- Steel version:
ghcr.io/steel-dev/steel-browser-api:latestandghcr.io/steel-dev/steel-browser-ui:latest(also reproduced on v0.4.4-beta)
Additional context
From logs and behavior, there may be two separate issues that combine into the blank UI:
-
CDP port mismatch or Chrome not ready
- API logs show ECONNREFUSED to
127.0.0.1:9222for CDP. Docker docs expose debugger on 9223 by default (docs). If the API expects 9222 inside the container while the published/forwarded port is 9223, or Chrome hasn’t bound yet, the CDP upgrade fails and the UI remains in a perpetual "connecting" state.
- API logs show ECONNREFUSED to
-
UI uses
ws://0.0.0.0:3000/as a target- The cast socket error points to
0.0.0.0, which is a wildcard bind address, not a routable host. When used as a client endpoint, this can causeECONNRESET/hang-ups. The UI should target the actual host (e.g.,http://localhost:3000), not0.0.0.0.
- The cast socket error points to
Potential workarounds I tried/consider:
- Restarting the API container temporarily restores the UI until the next occurrence.
- Ensuring the API/UI are accessed via
localhostand not an IP alias (still reproduced). - Verifying ports: API
3000, debugger9223, UI5173per docs. Reference: Docker setup.
What would help:
- Guidance on configuring the CDP port the API expects (9222 vs 9223) in self-hosted Docker, or making the service retry/backoff until Chrome is ready.
- UI fix to avoid using
0.0.0.0as a client WebSocket URL; prefer the actual host/origin. - If this is an expected session end, surface a clear error instead of an infinite "connecting" state.
References
- Docker/self-hosting ports and services: https://docs.steel.dev/overview/self-hosting/docker
- Sessions/CDP connectivity: https://docs.steel.dev/overview/sessions-api/quickstart