You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Detect dead websocket connections faster after network changes instead of showing Working for up to 5 minutes #17003
What terminal emulator and version are you using (if applicable)?
No response
What issue are you seeing?
When the network changes underneath an active Codex session, for example switching VPNs or after resuming from sleep, the TUI can keep showing Working for 5 minutes even though the underlying websocket is effectively dead.
The websocket response stream is considered dead only after stream_idle_timeout_ms, which defaults to 300000 ms (5 minutes).
I traced my Linux suspend/resume stalls to IPv6 address rotation.
After resume, NetworkManager has removed the WebSocket's source address, but
Linux still reports the TCP socket as established (SO_ERROR=0, no EOF). Codex
reuses it and waits for the 300-second timeout.
Ragnoroct/codex@082a5fc is a diagnostic-only commit. It checks the socket's local IP before each send with a temporary UDP bind (no traffic):
before suspend: assigned
after resume: stale (EADDRNOTAVAIL)
Linux, codex-cli 0.157.1: same stall after suspend/resume. Tracing at 18344a9:
ResponsesWebsocket::is_closed() only checks whether the pump task has finished (codex-api/src/endpoint/responses_websocket.rs:225-231), so a half-open socket passes the reuse check in core/src/client.rs:1562-1571.
The client never sends pings, and incoming pongs are dropped (responses_websocket.rs:91-97).
connect_resolved_tcp sets only TCP_NODELAY (websocket-client/src/dialer.rs:209-217).
1. Probe before reusing an idle socket (gives recovery in seconds).
Add WsCommand::Ping { tx_pong: oneshot::Sender<()> } to the pump. The pump sends Message::Ping and completes tx_pong on the next Message::Pong, instead of dropping it.
Add ResponsesWebsocketConnection::probe(deadline) -> bool, false if no pong arrives within about 2 s.
In the reuse check, when the connection has been idle more than about 15 s, measured on a clock that advances during suspend (CLOCK_BOOTTIME on Linux, SystemTime elsewhere), call probe. On false, reset with reason "probe_failed" and reconnect.
The first turn after wake then reconnects within about 2 s instead of 300 s, and turns on a recently used connection pay nothing.
This bounds a send over a dead path to about 20 s and lets keepalive reap idle dead sockets. On its own it doesn't give seconds-level recovery, hence layer 1. socket2 0.6 is already a workspace dependency.
// Linux psuedocode: RTMGRP_IPV6_IFADDR + RTM_DELADDR
let ip = conn.local_addr()?.ip();
listen(RTMGRP_IPV6_IFADDR, |event| {
if event.kind == RTM_DELADDR && event.ip == ip {
reconnect(conn);
}
});
I hope this gets fixed. my workflow involves re-connecting various devices posing as network adapters etc. even though I have a seperate one for stable internet connection. a single, irrelevant to internet access, netlink event moves the Codex websocket into a zombie state every single time.
Activity
github-actions commentedon Apr 7, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
fix(codex): three ergonomic defaults from the OpenAI prompting resear…
fix(codex): plumb timedOut/idleTimedOut flags so embedded-runner retr…
Ragnoroct commentedon Jul 29, 2026
I traced my Linux suspend/resume stalls to IPv6 address rotation.
After resume, NetworkManager has removed the WebSocket's source address, but
Linux still reports the TCP socket as established (
SO_ERROR=0, no EOF). Codexreuses it and waits for the 300-second timeout.
Ragnoroct/codex@082a5fc is a diagnostic-only commit. It checks the socket's local IP before each send with a temporary UDP bind (no traffic):
argothiel commentedon Sep 13, 2026
Same issue after simple sleep/resume on codex-cli 0.154.0.
mib00038 commentedon Sep 27, 2026
Linux, codex-cli 0.157.1: same stall after suspend/resume. Tracing at 18344a9:
ResponsesWebsocket::is_closed()only checks whether the pump task has finished (codex-api/src/endpoint/responses_websocket.rs:225-231), so a half-open socket passes the reuse check incore/src/client.rs:1562-1571.responses_websocket.rs:91-97).connect_resolved_tcpsets onlyTCP_NODELAY(websocket-client/src/dialer.rs:209-217).stream_idle_timeout(300 s) on tokio's monotonic clock, which stops during suspend, so the full 300 s runs after wake. Allow idle threads to prewarm and repair WebSocket connections #47701's idle repair also relies onis_closed().Proposed fix, two layers:
1. Probe before reusing an idle socket (gives recovery in seconds).
WsCommand::Ping { tx_pong: oneshot::Sender<()> }to the pump. The pump sendsMessage::Pingand completestx_pongon the nextMessage::Pong, instead of dropping it.ResponsesWebsocketConnection::probe(deadline) -> bool, false if no pong arrives within about 2 s.CLOCK_BOOTTIMEon Linux,SystemTimeelsewhere), callprobe. On false, reset with reason"probe_failed"and reconnect.2. Kernel backstop in
connect_resolved_tcp:This bounds a send over a dead path to about 20 s and lets keepalive reap idle dead sockets. On its own it doesn't give seconds-level recovery, hence layer 1.
socket20.6 is already a workspace dependency.We haven't tested a patch.
Ragnoroct commentedon Sep 27, 2026
On linux it can be a lot simpler.
ninja- commentedon Oct 4, 2026
I hope this gets fixed. my workflow involves re-connecting various devices posing as network adapters etc. even though I have a seperate one for stable internet connection. a single, irrelevant to internet access, netlink event moves the Codex websocket into a zombie state every single time.