Repository navigation
[Bug][Codex] macOS Codex 0.152.0 streams disconnect through ocx 2.39.0 #3245
Description
Activity
- addedbugSomething isn't workingSomething isn't workingcliCLI, config inject, packaging flagsCLI, config inject, packaging flagsplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)OS/service/tray/ACL (Windows-heavy, not Windows-only)serviceService lifecycle (WinSW/launchd/scheduler)Service lifecycle (WinSW/launchd/scheduler)streamingSSE, WebSocket, terminal stream framesSSE, WebSocket, terminal stream frames
on Sep 2, 2026 리뷰 · 우선순위 66 / 80
이 이슈는 macOS에서 Codex CLI 0.152.0이 네이티브 OpenAI 경로로는 같은 최소 프롬프트가 성공하는데, OpenCodex 2.39.0(
http://127.0.0.1:10100/v1)으로 라우팅하면 1차 모델과 3차 모델 모두stream disconnected before completion: error sending request for url (http://127.0.0.1:10100/v1/responses)로 다섯 번 재시도 후turn.failed한다는 보고입니다. 지금 로컬devHEAD는7d25f9966(docs(cursor): dashboard section and read-only surface note (#3249), package 2.40.0) 입니다. 보고 버전(2.39.0)과 HEAD(2.40.0) 사이에 Responses 쪽으로는 업스트림 WS 옵트인(#3216), 아웃바운드 body ceiling(#3196), spark namespace scrub(#3224/#3234), combo zero-output failover(#3236) 등이 들어갔지만, Darwin tee 기본 정책 자체를 바꾼 커밋은 없습니다.보고에 나온 426 → HTTP 폴백은 지금 코드에서도 정상 동작입니다.
src/server/index.ts에서/v1/responsesWebSocket 업그레이드가 오면websocketsEnabled(config)가 꺼져 있을 때 426upgrade_required를 돌려 줍니다. 주석대로 Codex는 이걸 세션 범위 HTTP 폴백으로 매핑합니다. 닫힌 #324가 기대한 동작이고, 보고자도websockets: false라고 적었습니다. 따라서 426 자체는 버그가 아닙니다. 진짜 문제는 폴백 뒤 HTTP SSE가 끝나기 전에 클라이언트 쪽 연결이 끊기는 부분입니다.문서
docs-site/src/content/docs/guides/codex-integration.mdProxy connection errors 절은 같은 문자열을 “프록시가 안 떠 있음”으로 안내합니다. 그런데 이 보고의 대조 실험은 그 설명을 이미 반박합니다. 같은 머신에서 직접POST /v1/chat/completions·POST /v1/responses(zai, stream)가[DONE]/response.completed까지 가고, 네이티브(주입 전) Codex exec도 성공합니다. launchd와ocx start포그라운드 A/B에서도 재현되므로 launchd 환경 상속만의 문제로 보기 어렵습니다. 보고자 말대로 Codex CLI가 보내는 전체 Responses 요청 모양과 브리지/릴레이 경로를 의심하는 게 맞습니다.역사적으로 가까운 이슈는 #1127(Darwin에서 rewrite/tee 경로가 클라이언트로 SSE를 못 넘기던 문제, #1142로 랜딩·닫힘)과 #1809(커스텀 프로바이더용 terminal repair 탈출구, 관련 PR #2362는 아직 open)입니다. 지금 HEAD의 Darwin 정책은
src/lib/bun-stream-caps.tsselectEagerPath기준입니다. Windows는 no-rewrite면 eager 가능, Darwin은streamMode: "eager-relay"명시 opt-in일 때만 eager이고auto는 계속 tee입니다.src/server/responses/core.ts주석도 같은 말을 합니다. #1127 증상은 “업스트림 200인데 클라이언트가 이벤트 0개 + hang”에 가깝고, 이번 보고는 “재시도 후 stream disconnected / error sending request”라서 완전히 같은 버그라고 단정하면 안 됩니다. 다만 macOS + Bun 1.4.0 + Responses passthrough라는 같은 무대 위에서 일어나는 일이라 조사 출발점으로 쓸 만합니다.추가로 보고 환경에는 아웃바운드 프록시
http://127.0.0.1:1082가ocx config set proxy로 켜져 있습니다. OpenCodex 쪽은 루프백을NO_PROXY에 남기는 설계(src/types/config.ts/src/lib/provider-outbound.ts)이고, chatgpt.com usage 401 확인도 있어서 “업스트림 프록시가 완전 죽음”은 아닙니다. 그래도 Codex CLI 프로세스 자체에HTTP_PROXY/HTTPS_PROXY가 새어 들어가면, 어떤 요청은 127.0.0.1:10100을 프록시로 우회하려다error sending request형태로 깨질 수 있습니다. 직접 curl은 프록시 없이 성공하고 Codex만 실패하는 패턴과 잘 맞습니다. 먼저 Codex 쪽 환경에서 루프백 NO_PROXY와 프록시 상속을 가르는 실험이 필요합니다.경로
src/server/index.tsWS 426 게이트 - 보고의 426 폴백과 일치. 버그 후보 아님.
경로src/lib/bun-stream-caps.tsselectEagerPath- Darwinauto는 tee. 재현 환경에서streamMode: "eager-relay"한 번 켜 보면 경로 분기 단서가 됩니다.
경로src/server/responses/core.tspassthrough SSE tee/eager 분기 - Codex 풀 요청이 rewrite를 켜면 Darwin에서 tee+JS-pull로 갈 수 있음. #1127 계열과 겹치는지 request-log로 확인 필요.
경로src/server/responses/ws-upstream.ts/ #3216 - 업스트림 WS는 옵트인. 보고는 클라이언트면 WS 비활성. 원인 후보 우선순위는 낮음.
환경 Codex CLI vs 최소 curl - 같은 포트에서 curl Responses는 성공, Codex 모양만 실패. 본문·헤더·세션·도구 스키마 차이가 핵심 가설.
버전 갭 2.39.0 → HEAD 2.40.0 - 즉시 Darwin 스트림 수정은 없음. 그래도 재현을 2.40.0/dev에서 한 번 더 확인하면 이미 고친 인접 회귀인지 가릴 수 있음.메인테이너의 판단이 필요한 지점
- 끊김이 업스트림
response.created전인지 후인지(요청 번역 vs 단말 이벤트 전달)를 로그로 분류할지. - Codex 프로세스에 아웃바운드 프록시 env가 새는지 vs OpenCodex 내부 릴레이만의 문제인지 먼저 가를지.
- Darwin에서
streamMode: "eager-relay"실험 결과를 정식 재현 매트릭스에 넣을지. - #893 is not fixed on macOS: #1025 repairs snapshots but the transport gate from closed #928 is still missing — Codex hangs on third-party Responses gateways #1127/[Compatibility] Add an opt-in Responses stream/terminal repair escape hatch for custom providers #1809/#2362와 묶을지, 새 macOS+Codex 0.152 전용 조사 이슈로 둘지.
- 보고자에게 2.40.0(또는 현재
dev) 재현 +ocxrequest-log 한 턴 첨부를 필수 needs-info로 둘지.
너의 추천
닫지 말고 조사 이슈로 유지한다. types.ts/config.ts 분할과 무관하고 중복 클로즈 대상도 아니다. 다음 구체 스텝: (1) 같은 재현을 2.40.0에서 한 번 더, (2) Codex 실행 환경에서HTTP_PROXY/NO_PROXY를 비운 A/B, (3) OpenCodex request-log에서 해당 턴의 status·첫/끝 SSE 이벤트·upstream 상태 확인, (4) 가능하면streamMode: "eager-relay"A/B. 이 네 결과가 오기 전에는 코드 수정 PR을 열지 말고 needs-info로 분류 데이터를 받는 편이 안전하다.이 댓글은 grok-bot이 작성했습니다
- 끊김이 업스트림
Vontean commented
on Sep 2, 2026 AuthorMore actionsI found a local workaround and some additional transport-level evidence.
On the affected setup (Codex 0.152.0 / OpenCodex 2.39.0 / bundled Bun 1.4.0 / macOS 26.6.2), enable the OpenCodex Responses WebSocket endpoint:
ocx config set websockets true ocx service repair ocx ready --wait --timeout 60
After this change, both of these full Codex requests completed on the first attempt with
OKandturn.completed:gpt-5.6-lunacommand-code/deepseek-deepseek-v4-flash(about 162k input tokens)
Before enabling WebSockets, I inserted a localhost transport probe between Codex and OpenCodex. It observed the WebSocket upgrade request and OpenCodex's expected HTTP 426 response. Codex then logged
falling back to HTTP, but in this environment the probe observed no subsequent HTTP POST before Codex reportedstream disconnected before completion. The failed attempts also did not appear as/v1/responsesrequests in the OpenCodex usage log.This suggests the failure may be in the Codex 0.152.0 426-to-HTTP fallback path, rather than necessarily in the OpenCodex Responses bridge. This is an observation from one environment, not yet a confirmed upstream root cause.
I also tested the following without success and reverted them:
streamMode=eager-relay- forcing the OpenAI provider to HTTP/1.1
- disabling Codex zstd request compression
- disabling the Bun request timeout earlier in the route
Rollback:
ocx config set websockets false
Enabling WebSockets is a temporary workaround until the 426 fallback behavior is fixed or further explained.
The new localhost-probe result materially narrows the boundary. If the probe received the WebSocket upgrade and OpenCodex's 426, but received no subsequent HTTP
POST /v1/responsesand OpenCodex recorded no request, the failing HTTP attempt never entered the OpenCodex data plane. That rules out the OCX SSE relay as the cause of this specific reproduced attempt.I checked the released Codex source. In
rust-v0.152.0,codex-rs/core/src/client.rsrecognizes HTTP 426 asFallbackToHttpand is then supposed to callstream_responses_apiimmediately. The stablerust-v0.152.1release has the same fallback code, and its three release commits do not change this client path. The observation therefore points to a Codex-side failure between selecting fallback and issuing the HTTP request, not to a known 0.152.1 fix.OpenCodex should not work around this by accepting WebSocket upgrades when
websockets: false; that would violate the operator's explicit transport setting. Enablingwebsockets: trueis a valid opt-in workaround for this environment because it avoids the broken fallback path.Please retest the same probe on Codex 0.152.1. If no HTTP POST appears there either, the next actionable step is an upstream
openai/codexreport containing the minimal 426-recording probe and these three observations: the upgrade receives 426, Codex logsfalling back to HTTP, and no HTTP request reaches the probe. Link that report here. We should keep this issue open as upstream-tracking/needs-info rather than patching the OpenCodex relay without an incoming request.- addedneeds-infoWaiting on reporter for a concrete spec or reproductionWaiting on reporter for a concrete spec or reproductionupstream-trackingBlocked on an upstream (Codex CLI/Desktop) fix; kept open for discoverabilityBlocked on an upstream (Codex CLI/Desktop) fix; kept open for discoverability
on Sep 2, 2026 Investigated this against the current
devtree. Reporting what the code shows, because it moves the question off OpenCodex and into the Codex client's fallback path.The 426 is deliberate.
src/server/index.ts:1107-1126returns 426 when WebSockets are disabled. The Responses data plane only begins on the subsequent POST (src/server/index.ts:1755-1787).Your probe shows no POST and no usage-log entry. That matters: it means the SSE relay, terminal repair, idle timeout, and outbound connection reuse were never reached at all. Those live at
src/server/responses/core.ts:4657-4675andsrc/lib/upstream-retry.ts:294-311, and none of them can be responsible for a disconnect that happens before the first POST.The 426 → HTTP fallback is Codex-side and already covered upstream.
codex-rs/core/src/client.rs:2048-2064routes 426 to HTTP, andcodex-rs/core/tests/suite/websocket_fallback.rs:34-80asserts exactly one resulting POST.Our control test predates 2.39.0 and passes.
tests/server-auth.test.ts:1384-1422asserts 426 followed by HTTP 200.So there is no OpenCodex-side change that would be honest to make here. What would settle it: re-run on Codex 0.152.1 or later and record
HTTP_PROXY,HTTPS_PROXY,NO_PROXY, and the localhost probe alongside the failure. If the HTTP POST is still absent with those recorded, the fix belongs in the upstream client's fallback (fresh request, proxy bypass, session state), and this issue is the right place to track it.Keeping
upstream-trackingandneeds-info.@Vontean — following up during a backlog review. This is not closed; it needs one piece of information from you to move.
Where this stands. OpenCodex returns 426 deliberately at
src/server/index.tsand the HTTP fallback path is covered bytests/server-auth.test.ts. Your own probe showed no subsequent POST, which places the failure upstream in the Codex client rather than in the proxy.What would unblock it. Please add whether this still reproduces on a current Codex build and a current ocx — and if so, an
ocx logs --jsonlexcerpt spanning the disconnect.If that arrives, this gets picked up in the next triage pass. Without it, the report cannot be distinguished from configurations that already work, so it will keep sitting here rather than being wrong on purpose.
Following up during the 260904 backlog review. Still open, still
needs-info, and the ask has not changed — but the version gap has widened enough to be worth stating plainly.This was filed against OpenCodex 2.39.0.
devis now on the 2.43.0 line, and several things in the streaming and Responses path have moved since, including work on mid-stream socket handling. A "stream disconnected before completion" against a build that far back cannot be attributed to current code, and I would rather not guess at which of the intervening changes is relevant.The single most useful thing you can do is re-test on current: does the same minimal request still fail through OpenCodex 2.42.x or later, with the same Codex CLI version? If it passes, say so and I will close this with that as the record. If it still fails, the same A/B you already ran — but on the new version — tells us it is a live defect rather than a fixed one, and I will treat it as such.
If it is easier,
ocx debug provider onaround one failing turn plus the resultingrun-requestentry would be more informative than the client-side error, which only reports that the socket ended.The
upstream-trackinglabel is on this because a Codex CLI change is a plausible cause and we do not control that side. If there is no reply,stale-needs-infowill eventually close it, which is the honest outcome for a report we cannot reproduce or attribute — not a judgment that it was invalid.Still blocked on reporter evidence, and the code-side search is exhausted rather than merely unfinished.
The decisive detail is in your own probe: there was no POST and no usage-log entry. The Responses data plane does not start until the subsequent POST (
src/server/index.ts:1755-1787), so the SSE relay (src/server/responses/core.ts:4657-4675), terminal repair, idle timeout, and outbound connection reuse (src/lib/upstream-retry.ts:294-311) were never reached. None of them can explain a disconnect that happens before the first POST.The 426 is intentional —
src/server/index.ts:1107-1126returns it when WebSockets are disabled, and you reportedwebsockets: false. The 426-then-HTTP fallback is client-side and covered upstream (codex-rs/core/src/client.rs:2048-2064), and our side of that contract is green:tests/server-auth.test.ts:1384-1422asserts 426 followed by HTTP 200.The version gap has also widened — you reported 2.39.0 and
devis now 2.43.0. Several streaming and Responses changes landed in between, though none altered the Darwin tee policy itself.Guessing at a fix from here would produce a change nobody could prove fixed anything. What would unblock it:
- Does the same failure reproduce on 2.42.x or later?
HTTP_PROXY/HTTPS_PROXY/NO_PROXYas set in the failing shell.- A
run-requestentry withocx debug provider on, or anocx logs --jsonlexcerpt covering the failing turn — specifically whether a POST leaves the client at all.
Item 3 is the one that matters most: it distinguishes "the proxy mishandled the request" from "the request never arrived", and right now the evidence points at the second.
Closing as stale needs-info. This was filed against 2.39.0 and
devis now on the 2.49.0 line, with
substantial streaming and Responses changes in between, so a disconnect on that build cannot be
attributed to current code.Your own transport probe is what makes this the honest outcome rather than a guess. The upgrade
received the deliberate 426, Codex loggedfalling back to HTTP, and no subsequent
POST /v1/responsesreached the probe or the usage log. The OpenCodex Responses data plane does not
begin until that POST, so the SSE relay, terminal repair, idle timeout, and outbound connection reuse
were never reached and cannot explain the failure. The 426 to HTTP fallback is client-side, and our
half of the contract is covered by a test asserting 426 followed by HTTP 200.ocx config set websockets trueremains a valid opt-in for this environment.If it still reproduces on a current Codex CLI and a current
ocx, please reopen with an
ocx logs --jsonlexcerpt spanning the disconnect, or arun-requestentry captured with
ocx debug provider on— specifically whether a POST leaves the client at all. Thank you for the
localhost probe; it is the single most useful piece of evidence in this thread.
Client or integration
Codex CLI / native Codex integration through OpenCodex
Area
Streaming / Responses compatibility
Summary
On macOS, Codex CLI 0.152.0 can complete a first-party request when using the native OpenAI route, but the same minimal request repeatedly fails when Codex is routed through OpenCodex 2.39.0 at
http://127.0.0.1:10100/v1.Both a first-party model and a configured third-party model fail with:
The failure reproduces with the launchd service and with the documented foreground A/B command, so it does not appear to be only a launchd proxy-environment inheritance issue.
Environment
http://127.0.0.1:1082ocx integration native codex onwebsockets: falseSetup
All three provider checks succeeded:
ocx doctorreportsConfigured proxy: set,routing=opencodex-local, and Bun 1.4.0.Reproduction
First-party:
Third-party:
For both requests Codex first reports the expected client-facing WebSocket fallback:
Then it retries five times and ends with:
The same failure reproduces after restoring the launchd service and also with:
Controls
codex execrequest completed natively withOKandturn.completed.https://chatgpt.com/backend-api/wham/usagereturned HTTP 401 quickly, which confirms the connection path without exposing credentials.POST /v1/chat/completionswithzai/glm-5.3andstream: truecompleted withdata: [DONE].POST /v1/responseswithzai/glm-5.3andstream: truecompleted withresponse.completedanddata: [DONE].Therefore provider authentication, the local proxy, the upstream HTTP proxy, and minimal local SSE delivery all work. The failure seems specific to the full request shape emitted by Codex CLI and its Responses bridge/stream relay path.
Expected behavior
Codex should receive a complete streamed response and a terminal Responses event for both the first-party and third-party model requests.
Actual behavior
The local
/v1/responsesconnection is closed before Codex receives a terminal event. Codex retries five times and reportsstream disconnected.Related issues
This appears related to the historical Responses/macOS streaming compatibility issues in #1127 and #1809, but the current reproduction is on 2.39.0 with Codex 0.152.0 and also affects a minimal first-party request. #324 documents the expected 426 behavior when the client-facing WebSocket endpoint is disabled.
Could you please investigate the Codex 0.152.0 request shape and the OpenCodex Responses bridge/stream relay on macOS? A debug classification of whether the close occurs before or after upstream
response.createdwould also help distinguish request translation from terminal-event delivery.No credentials or tokens are included in this report. Bun was kept at 1.4.0; no runtime downgrade was attempted.