Skip to content

[Bug] Codex 계정 cooldown이 LM Studio 등 비-forward provider 요청까지 429로 차단합니다 #104

Description

@idguyst

아래 글은 gpt 5.6이 작성해줬어요.

gpt 주간할당량 다 써서 다른 모델로 변환하고 쓸라고 했더니 '429 Selected Codex account is cooling down' 이라고 나와서 gpt에게 분석시키고 아래 글을 작성하라고 했어요.


환경

  • OpenCodex: 2.7.8
  • API: POST /v1/responses
  • ChatGPT authMode: "forward" provider와 별도의 비-forward provider를 함께 사용
  • 재현한 비-forward provider: LM Studio(openai-chat), Umans(openai-chat)
  • HTTP와 WebSocket 경로 모두 영향 가능

현상

ChatGPT/Codex 계정이 사용량 제한으로 cooldown 상태가 된 직후, ChatGPT 인증을 사용하지 않는 별도 provider 모델로 전환해도 다음 오류가 반환됩니다.

429 Selected Codex account is cooling down

실제 로그에서는 LM Studio 요청이 약 2~26ms 만에 429로 종료됐습니다. 로컬 추론 서버에 도달하기 전 OpenCodex 내부에서 차단되는 패턴입니다.

재현 절차

  1. ChatGPT forward provider와 LM Studio 같은 routed provider를 함께 설정합니다.
  2. 활성 Codex 계정이 upstream 429를 받아 cooldown 상태가 되도록 합니다.
  3. 다음처럼 비-forward provider 모델을 요청합니다.
{
  "model": "lm-studio/local-model",
  "input": "hello",
  "stream": false
}
  1. LM Studio가 아니라 OpenCodex가 즉시 429를 반환합니다.

강제 cooldown을 설정한 동일 프로세스에서 순정 v2.7.8을 재현한 결과입니다.

gpt model              -> 429 Selected Codex account is cooling down
lm-studio/local-model  -> 429 Selected Codex account is cooling down
umans/model            -> 429 Selected Codex account is cooling down

기대 동작

  • authMode: "forward"인 ChatGPT/Codex 요청은 기존대로 cooldown 429를 반환해야 합니다.
  • 자체 API key 또는 로컬 서버를 사용하는 비-forward provider의 본 요청은 계속 실행돼야 합니다.
  • 비-forward 요청이 ChatGPT 기반 vision/web-search sidecar를 실제로 필요로 할 경우에만 Codex 인증 상태가 sidecar 가용성에 영향을 줘야 합니다.
  • Codex 인증이 실패하거나 cooldown이면 inbound Authorization 또는 chatgpt-account-id를 sidecar 인증으로 대신 사용하면 안 됩니다.

원인으로 보이는 부분

routeModel()로 실제 provider를 결정한 뒤에도 handleResponses()가 provider 종류와 관계없이 resolveCodexAuthContext()를 호출합니다.

resolveCodexAuthContext()는 선택된 Codex 계정이 cooldown이면 즉시 CodexAccountCooldownError를 던집니다.

반면 실제 Codex 계정 credential을 provider에 적용하는 applyCodexAuthContextToProvider()는 이미 provider.authMode === "forward"일 때만 동작합니다.

따라서 provider 적용 범위는 분리되어 있지만, 그보다 앞선 account cooldown gate가 모든 routed provider를 전역 차단합니다.

WebSocket 경로는 handshake 시점에는 아직 요청 모델이 없는데도 Codex auth context를 먼저 해석하므로, cooldown 상태에서는 이후 사용할 모델이 routed provider인지 확인하기 전에 upgrade 자체가 거부될 수 있습니다.

기존 보안 설계와의 관계

설계 문서상 Codex auth context를 routed provider와 독립적으로 해석하는 이유는, 비-forward provider도 vision/web-search sidecar에서 ChatGPT를 사용할 수 있기 때문으로 이해했습니다.

이 보안 목적은 유지돼야 한다고 생각합니다. 단순히 비-forward provider에서 Codex auth 오류를 무시하고 inbound bearer를 그대로 사용하면 proxy admission token이나 관계없는 credential이 ChatGPT sidecar로 전달될 수 있습니다.

/v1/images/* 구현에는 이미 forward auth 실패 시 독립된 keyed provider를 계속 사용할 수 있도록 유사한 분리가 적용되어 있습니다.

제안 수정 방향

HTTP Responses

  1. provider.authMode === "forward"이면 현재처럼 Codex auth/cooldown을 엄격하게 적용합니다.
  2. 비-forward provider에서는 Codex auth를 vision/search sidecar용 선택적 credential로만 해석합니다.
  3. 선택적 Codex auth가 cooldown, reauth 실패 또는 affinity 만료라면:
    • 본 routed provider 요청은 계속 실행합니다.
    • sidecar용 헤더에서 authorization과 chatgpt-account-id를 제거합니다.
    • 이미지 sidecar가 필수인 text-only 모델은 기존 fail-closed 이미지 제거 경로를 사용합니다.
  4. 알 수 없는 내부 오류는 삼키지 않고 기존처럼 전파합니다.

WebSocket

  1. 모델 정보가 없는 handshake에서는 Codex account cooldown 판정을 미룹니다.
  2. 각 response.create를 handleResponses()에서 실제 provider로 route한 뒤 HTTP와 동일한 규칙을 적용합니다.
  3. 결정된 auth context는 기존 WebSocket registry에 다시 기록합니다.
  4. GPT/forward turn의 cooldown·reauth·affinity 오류는 기존 standalone WS error frame으로 전달합니다.

제안 회귀 테스트

  1. Codex 계정을 강제 cooldown으로 만든 상태에서:
    • GPT/forward 요청은 429
    • routed LM Studio 요청은 200이며 실제 routed upstream에 도달
  2. 같은 상태에서 text-only routed 모델에 이미지를 첨부하면:
    • ChatGPT sidecar 호출 없음
    • inbound bearer가 ChatGPT endpoint로 전달되지 않음
    • 본 routed 요청은 기존 fail-closed 이미지 처리 후 실행
  3. WebSocket handshake는 cooldown 중에도 성공하고:
    • routed provider turn은 완료
    • GPT/forward turn만 429 error frame
  4. 기존 pool token refresh, expired affinity, vision sidecar 정상 경로 테스트가 계속 통과

로컬 검증 결과

위 방향으로 패치한 순정 v2.7.8 복제본에서 다음 검증을 통과했습니다.

TypeScript typecheck: pass
관련 테스트: 86 pass, 0 fail
신규 cooldown isolation 테스트: 2 pass, 0 fail

신규 테스트가 확인한 결과:

forced cooldown + GPT HTTP/WS       -> 429
forced cooldown + routed HTTP/WS    -> 200/completed
forced cooldown + unavailable sidecar auth -> ChatGPT sidecar 호출 0회

필요하다면 재현 테스트 또는 최소 패치 형태로 추가 정리하겠습니다.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions