Codex reconnects after SSE idle timeout with local MLX gpt-oss-120b despite Rapid-MLX keepalive comments
Resolved 💬 0 comments Opened Jul 8, 2026 by 66Ton99 Closed Jul 8, 2026
When using Codex CLI with a local Rapid-MLX /v1/responses endpoint and the MLX repack model 66ton99/gpt-oss-120b, long-running turns can stall until Codex starts reconnecting and eventually reports:
Reconnecting... 3/5 (18m 52s • esc to interrupt)
└ Stream disconnected before completion: idle timeout waiting for SSE
Model
- Hugging Face: https://huggingface.co/66ton99/gpt-oss-120b
- Runtime: Rapid-MLX / MLX on Apple Silicon
- API surface: OpenAI-compatible
/v1/responses
Observed behavior
- Codex starts a streaming
/v1/responsesrequest. - Rapid-MLX logs SSE keepalive activity while the model is still working.
- Codex still enters reconnect mode after a long idle period.
- Example user-visible error:
Reconnecting... 3/5 (18m 52s • esc to interrupt)
└ Stream disconnected before completion: idle timeout waiting for SSE
Expected behavior
- Codex should keep the stream alive while the server is still producing SSE keepalive frames, or the server should emit a Responses-compatible heartbeat event that Codex treats as activity.
- Long prefills / long reasoning phases for a 120B local model should not trigger reconnects as long as the server is alive and emitting heartbeat traffic.
Technical notes
- Codex has a stream idle timeout controlled by
stream_idle_timeout_ms; default is300000ms, i.e. 5 minutes. - The Codex SSE reader times out when no parsed SSE event arrives before that deadline.
- Rapid-MLX currently emits keepalive as SSE comments, e.g.
: keepalive\n\n. - SSE comments may keep the TCP connection/proxy alive, but Codex may not count them as
stream.next()events after EventSource parsing. - This means Rapid-MLX can be emitting keepalive comments while Codex still considers the Responses stream idle.
Likely root cause
Codex's idle watchdog is based on parsed SSE events, while Rapid-MLX keepalive uses SSE comments. Comments are valid SSE and useful for proxies, but they may be discarded by the parser before Codex's stream.next() activity watchdog sees them.