Skip to content

[Bug]: Existing Codex CLI thread bypasses pool over direct WebSocket while status reports opencodex-local (2.53.0) #4550

Closed

Description

@garysassano

Client or integration

Codex CLI

Area

Proxy and routing

Summary

An existing Codex CLI thread repeatedly hits the native ChatGPT usage limit while OpenCodex 2.53.0 reports routing=opencodex-local and its two-account pool has a working secondary account. Thread-correlated Codex diagnostics establish that the failing requests connect directly to wss://chatgpt.com/backend-api/codex/responses, bypassing the pool.

This initially looked like quota/affinity failover, but the failing thread's actual destination changes the diagnosis. Please investigate the active-client routing/configuration lifecycle and whether status can distinguish configured routing from the route retained by a running client.

Reproduction

Observed sequence; not yet a deterministic clean-start reproduction:

  1. Use Codex CLI 0.154.0 with ChatGPT login and an existing long-running thread using gpt-6-astra.
  2. Have OpenCodex 2.53.0 running locally with the canonical OpenAI provider in pool mode, two Plus accounts, quota strategy, and auto-switch threshold 95.
  3. The primary account reaches its quota while the secondary has substantial quota remaining. Manual secondary selection successfully serves other proxied requests.
  4. Run ocx status: live proxy health succeeds and routing is reported as opencodex-local.
  5. Continue the existing CLI thread. It repeatedly returns usage_limit_exceeded instead of reaching the pool.
  6. Correlate the thread's Codex SQLite diagnostics and session completion events: the Responses WebSocket destination is the direct ChatGPT endpoint. There is no corresponding proxy usage record for these turns.

Expected: the client uses the configured proxy route, or the integration clearly identifies that an existing client must be restarted/resumed to adopt it. A configured-route status should not be mistaken for proof that an active client is routed.

Actual: repeated direct upstream requests exhaust the native credential while a healthy local pool is available.

Version

OpenCodex 2.53.0, Codex CLI 0.154.0. The latest upstream release checked at reporting time is v2.53.0.

Operating system

Linux / WSL2

Provider and model

OpenAI ChatGPT login, gpt-6-astra, low reasoning effort.

Logs or error output

Sanitized extraction, with thread IDs, request IDs, account labels, headers, and credentials omitted:

2026-09-13 19:07:50 UTC
  target: codex_api::endpoint::responses_websocket
  destination: wss://chatgpt.com/backend-api/codex/responses
2026-09-13 19:07:51 UTC
  same thread task_complete.error.codex_error_info: usage_limit_exceeded

2026-09-13 19:08:41 UTC
  target: codex_api::endpoint::responses_websocket
  destination: wss://chatgpt.com/backend-api/codex/responses
2026-09-13 19:08:43 UTC
  same thread task_complete.error.codex_error_info: usage_limit_exceeded

CLI: You've hit your usage limit.

ocx status, checked during investigation:
  Proxy: running
  Health: http://127.0.0.1:10100/healthz ok (live)
  routing=opencodex-local, service=installed-but-unhealthy, shim=absent

The service warning is real: the systemd unit is inactive, but a separate live 2.53.0 proxy process answers health checks and serves other traffic. Restarting the proxy previously did not resolve the affected CLI thread.

The affected CLI process was associated with the exact session via its open rollout file. Its environment has no OPENAI_BASE_URL or CODEX_HOME override, and its command line has no base_url/model_provider override. No raw diagnostics are attached because they can contain sensitive headers.

Redacted configuration

Current root keys, verified by parsing the TOML:

openai_base_url = "http://127.0.0.1:10100/v1"
experimental_realtime_ws_base_url = "http://127.0.0.1:10100/v1"

No explicit root model_provider. Relevant OpenCodex settings:

{
  "autoSwitchThreshold": 95,
  "multiAgentMode": "v1",
  "providers": {
    "openai": {
      "authMode": "forward",
      "codexAccountMode": "pool"
    }
  }
}

accountPoolStrategy is omitted and defaults to quota in 2.53.0. pool.cacheAffinity is omitted. A secondary account is manually pinned; its identifier is intentionally omitted.

Scope and remaining uncertainty

  • Direct routing of the failed turns is confirmed. This report does not establish a defect in pool priority selection or quota failover.
  • The current config is confirmed, but its exact contents when the affected process started are not established. Retained pre-injection configuration is a possible explanation. A fresh-process reproduction remains necessary before claiming Codex 0.154.0 ignores openai_base_url.
  • Other proxied requests and manual secondary success do not prove this thread was proxied.
  • Earlier 429 usage records belong to other requests and are deliberately excluded as evidence for this thread.
  • The observed direct upstream error's wire representation is not established by the task-completion error alone.
  • Related closed issue: [Bug]: ocx status reports the proxy healthy while routing is native, so nothing actually routes #2411 concerned an absent override and missing routing status. Here the override exists and status explicitly says opencodex-local, while the active client's actual destination is direct.

Checks

  • I searched existing issues and documentation/source relevant to routing.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    bugSomething isn't working
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    proxyHTTP proxy, routing, reverse-proxy / management auth
    streamingSSE, WebSocket, terminal stream frames
    on Sep 13, 2026
  2. lidge-jun commented on Sep 13, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 74 / 80

    이 이슈는 Codex CLI가 OpenCodex 상태에는 routing=opencodex-local이라고 보이는데, 실제로는 이미 열려 있던 긴 스레드의 요청이 wss://chatgpt.com/backend-api/codex/responses로 바로 가서 계정 풀의 두 번째 계정을 쓰지 못한다는 보고입니다. 표면 증상은 “쿼터가 안 넘어간다”처럼 보이지만, 보고자가 스레드 진단으로 목적지가 ChatGPT 직행 WebSocket임을 확인했기 때문에 진짜 질문은 풀 알고리즘이 아니라 클라이언트가 어떤 라우팅을 붙잡고 있는가입니다.

    지금 dev HEAD e30f1d27e 기준으로도 이 구분은 중요합니다. OpenCodex 쪽 풀·어피니티·재시작 버전 스큐(#4545)는 프록시를 통과하는 요청에만 의미가 있습니다. CLI가 예전 스레드에 묶인 native ChatGPT 연결을 그대로 쓰면, 상태 API가 말하는 “로컬 프록시 라우팅”과 실제 바이트 경로가 어긋납니다. 보고 버전은 2.53.0이고 CLI는 0.154.0입니다. 재현은 깨끗이 처음부터 돌린 시나리오는 아직 아니고, 관찰 순서입니다.

    관련 코드는 풀 선택(src/codex/routing.ts)보다, 클라이언트가 주입·루프백·커스텀 베이스 URL을 어떻게 유지하는지(src/codex/loopback-target.ts, src/cli/codex-shim-readiness.ts의 routingKind, 상태/doctor 쪽)에 가깝습니다. “status는 opencodex-local인데 wire는 chatgpt.com”이면, 상태 문구가 설정값을 말하는지 살아 있는 클라이언트 세션을 말하는지부터 갈라야 합니다. 그렇지 않으면 메인테이너가 풀 버그로 오진하고 잘못된 수정을 합니다.

    이 이슈는 types/config 분할에 직접 묶이지 않습니다. 계정 풀·프록시·스트리밍 라벨이 이미 붙어 있어 분류는 맞습니다. 우선순위가 높은 이유는, 풀이 “정상인데도” 사용자 체감이 막히고, 상태 UI가 거짓 안심을 주기 때문입니다. 다만 재현이 아직 비결정적이라 바로 패치 PR을 받기보다 진단 체크리스트를 먼저 고정하는 편이 낫습니다.

    다음 확인이 필요합니다. (1) 해당 스레드를 연 시점의 config·inject 상태, (2) ocx doctor/ocx inspect가 같은 순간에 뭐라고 했는지, (3) 새 스레드에서는 풀이 도는지, (4) 프록시 재시작·CLI 재시작 후 같은 스레드 ID를 이어 쓰면 목적지가 바뀌는지. 이게 나와야 “설정과 세션 불일치”인지 “특정 스레드만 직행”인지 갈립니다.

    경로 src/codex/routing.ts - 이 이슈의 1차 용의자로 단정하면 안 된다. 보고된 wire는 풀을 거치지 않는 ChatGPT 직행이다.
    경로 src/cli/codex-shim-readiness.ts (routingKind) / status 출력 - 설정된 라우팅과 살아 있는 클라이언트 경로를 한 문구로 합치면 이번처럼 오진이 난다.
    심볼 wss://chatgpt.com/backend-api/codex/responses - 재현 증거로 유지하고, 같은 시각의 로컬 프록시 액세스 로그 유무와 대조해야 한다.
    버전 2.53.0 vs 현재 dev 2.54.0 - 보고 버전과 HEAD가 다르다. 재현 시 2.54.0에서도 같은지 한 줄만 확인해 달라고 요청할 가치가 있다.

    메인테이너의 판단이 필요한 지점

    • 상태를 “설정값”과 “활성 클라이언트 경로”로 나누어 보여줄지.
    • 긴 스레드가 native로 열린 뒤 프록시를 켠 경우의 지원 범위(재주입 필수인지, 자동 재부착인지).
    • 이 이슈를 풀 failover 계열(account-pool)에 둘지, 클라이언트 lifecycle/inject 계열로 옮길지.

    너의 추천

    • 바로 코드 수정 PR을 받기보다, 위 진단 4항을 이슈에 체크리스트로 달고 새 스레드 vs 기존 스레드 대조 로그를 받은 뒤 착수하세요. 풀 패치부터 가면 빗나갈 가능성이 큽니다.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Sep 14, 2026

    @lidge-jun
    Owner

    Partly landed on dev by 1de9184. Leaving this open, because only the reporting half is fixed.

    What landed: src/codex/routing-adoption.ts and autostart-health.ts now name a pre-injection matched CLI as pending-client-restart with its pid, keep adopted as an explicit inference rather than a claim, and leave the routing string byte-identical for the adopted and unknown cases. So when a stale CLI process is found, the status is accurate instead of asserting opencodex-local for a session that is not pooled.

    What did not land: the transport itself. An existing thread on a resumed direct WebSocket still bypasses the pool, and when the process matcher misses a process the status is still not proof about live traffic. Honest reporting is not interception, and this issue is about the bypass.

  4. lidge-jun commented on Sep 14, 2026

    @lidge-jun
    Owner

    Fixed on dev via #4581 at 1de9184.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't workingproxyHTTP proxy, routing, reverse-proxy / management authstreamingSSE, WebSocket, terminal stream frames

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions