Skip to content

Handle provider input size limit reached gracefully in codex #3170

Closed

Description

@DamnUi

Area

Streaming

What are you trying to accomplish?

In models like kimi k3, when the providers input size limit is reached, gracefully handle it instead of blocking the chat from working at all.
This way when it takes more than its allowed it should let the chat continue leaving a message such as reducing the context.

What prevents this today?

This completely breaks a chat from functions when it happens

Image

Example in codex above, any more requests i send in this all are rejected.

What should OpenCodex do?

Handle it gracefully like opencode same prompt:

Image

Allows to keep texting, im guessing by discarding the prompt and the images etc.

Example usage or interface

Just works after this is implemented doesnt completely stop chats

Alternatives or workarounds

Only alternative is to fork before sending this command in codex and then a/b testing what to send and what not, it is an extremely tedious process.

Additional context

No response

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

  1. github-actions commented on Sep 1, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  2. github-actions commented on Sep 1, 2026

    @github-actions
    Contributor

    Potential duplicates found:

    Reason: Duplicate of 2511: both request an opt-in per-provider request byte budget / downscaling/pruning discarding images/context so chats survive provider hard input-size limits rather than becoming permanently unusable.

    Detected automatically via GitHub Copilot; automatic closure requires separate deterministic evidence.

  3. DamnUi commented on Sep 1, 2026

    @DamnUi
    ContributorAuthor

    would say that is slightly different from the that other thread. As this does not just happen with images, in an earlier example it happened with text too but only in this model.

  4. Ingwannu commented on Sep 1, 2026

    @Ingwannu
    Owner

    Confirmed against current dev and the attached Codex failure. The immediate bug is not just that the provider rejected a large turn: OpenCodex relays a pre-stream HTTP 413, Codex classifies that as a retryable unexpected HTTP status, and the same oversized turn is sent again through the full 5/5 reconnect loop.

    Current codex-rs has a narrower recovery contract: a terminal Responses response.failed event whose error.code is context_length_exceeded becomes the non-retryable context-window error. Codex then marks the context as full, allowing its next-turn compaction policy to run. A raw HTTP 413 never reaches that path.

    I am going to scope the fix to that protocol boundary:

    • for streaming Responses requests, convert a confirmed upstream/local request-size 413 before SSE starts into one terminal response.failed event with bounded, redacted context_length_exceeded error data;
    • do not retry/fail over the same oversized payload;
    • cover passthrough and translated adapter paths, and ensure unrelated HTTP errors keep their current status/body fidelity;
    • keep non-streaming API behavior explicit and non-retryable rather than claiming silent recovery.

    I will not make the proxy silently delete user prompts or images. OpenCodex does not own the Codex transcript, and doing that server-side would be lossy and could hide which inputs were never analyzed. The safe continuation mechanism is to give Codex the error semantics it already understands so its own compaction/history policy can act on the next turn. I will link the focused PR and exact validation here once it is ready.

  5. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 74 / 80

    이 이슈는 공급자가 입력 크기 한도를 넘었다고 거절했을 때 OpenCodex가 같은 큰 요청을 계속 다시 보내지 말고, Codex가 알아들을 수 있는 “문맥이 꽉 찼다” 종료 신호로 바꾸자는 요청입니다. 지금 첨부된 사례처럼 첫 응답이 스트림 시작 전 HTTP 413으로 끝나면 Codex는 이를 일반적인 예상 밖 HTTP 오류로 보고 5/5 재연결을 시도합니다. 사용자는 새 메시지를 보내도 같은 큰 문맥이 다시 붙기 때문에 대화가 완전히 막힌 것처럼 보입니다.

    현재 dev에는 이미 모델의 알려진 문맥 한도보다 입력 추정치가 크게 넘으면 미리 막는 checkInputAdmission이 있습니다. 하지만 이 경로는 src/server/responses/core.ts에서 JSON 형태의 HTTP 413을 바로 돌려줍니다. 공급자에게 실제 요청을 보낸 뒤 413을 받은 경로도 오류 본문을 안전하게 읽고 정리한 다음 같은 HTTP 상태로 돌려줍니다. 즉, “너무 크다”는 판단 자체는 있지만 스트리밍 Responses 클라이언트가 다음 동작을 결정할 수 있는 프로토콜 신호가 아직 없습니다.

    중요한 점은 요청을 조용히 잘라내는 기능이 아니라 오류의 뜻을 정확하게 전달하는 기능이라는 것입니다. OpenCodex가 사용자 문장이나 이미지를 몰래 버리면 사용자는 무엇이 모델에 전달되지 않았는지 알 수 없습니다. 반대로 스트리밍 요청에 한해 종료 response.failed 이벤트와 context_length_exceeded 코드를 보내면 Codex가 자신의 기록과 압축 정책을 사용해 다음 턴을 정리할 수 있고, 같은 큰 요청의 재연결 반복도 멈출 수 있습니다.

    #2511과 겉으로는 비슷하지만 그대로 중복은 아닙니다. #2511은 공급자별 바이트 예산을 설정하고 이미지를 미리 축소하거나 제거하는 선택 기능이고, 이 이슈는 이미지뿐 아니라 긴 텍스트도 포함한 실제 413을 Codex가 이해하는 실패 신호로 바꾸는 문제입니다. 따라서 #3170은 별도로 유지하되, #2511은 나중에 선택적인 사전 축소 기능으로 서로 연결해 두는 편이 정확합니다.

    또한 모든 413을 무조건 같은 뜻으로 바꾸면 안 됩니다. 관리 API의 업로드 제한이나 허브 릴레이 제한처럼 Responses 입력 문맥과 관계없는 413도 있기 때문입니다. 변환 위치를 Responses 데이터 경계로 좁히고, 클라이언트가 스트림을 요청했으며 로컬 입력 허용 검사 또는 실제 공급자 요청 크기 거절로 확인된 경우만 바꾸어야 합니다. 오류 메시지는 기존 redactSecretString과 길이 제한을 통과시켜 공급자가 되돌려 준 요청 조각이나 토큰이 클라이언트로 새지 않게 해야 합니다.

    src/server/responses/core.ts:3092-3106 - 로컬 checkInputAdmission 거절이 스트리밍 요청에도 일반 JSON HTTP 413으로 끝나서 Codex의 문맥 초과 처리 경로에 들어가지 못하는 문제입니다.
    src/server/responses/core.ts:5949-5965 - Anthropic 이미지 요청은 이미 한 번 더 작은 이미지 단계로 재시도하므로 이 안전한 1회 복구를 없애지 말고, 그 재시도까지 413으로 실패한 뒤에만 문맥 초과 종료 신호를 만들어야 합니다.
    src/server/responses/core.ts:5968-6037 - 실제 공급자의 비정상 응답을 모두 HTTP 오류로 정리하는 공통 경로라서, 스트리밍 Responses의 확인된 크기 초과만 먼저 분기하지 않으면 같은 큰 요청의 재연결 반복이 계속됩니다.
    src/bridge.ts:2015-2038 - formatErrorResponse는 여러 일반 HTTP 오류가 함께 쓰는 함수이므로 여기에서 413 전체의 형식을 바꾸면 다른 API까지 영향을 받습니다. 전용이고 범위가 좁은 response.failed 생성기를 두는 편이 안전합니다.
    src/combos/failover.ts:151-172 - context_length_exceeded는 다음 후보로 넘기지 않고 멈추도록 이미 정해져 있습니다. 새 종료 신호도 이 구조화된 코드가 유지되어 같은 과대 입력이 다른 조합 대상에 반복 전송되지 않는지 확인해야 합니다.

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

    • 로컬 input_admission_refused도 스트리밍 Codex 요청에서는 context_length_exceeded로 보여 줄지, 실제 공급자 413만 변환할지 정해야 합니다. 사용자 증상과 재연결 방지는 둘 다 같은 신호가 필요해 보입니다.
    • 실제 공급자의 413을 확인할 때 HTTP 상태만 볼지, 알려진 request_too_large 코드나 본문 분류까지 요구할지 정해야 합니다. 너무 엄격하면 공급자별 문구 차이로 증상이 남고, 너무 넓으면 Responses와 무관한 413을 잘못 분류할 수 있습니다.
    • 비스트리밍 요청은 기존 HTTP 413 JSON을 유지할지 정해야 합니다. Codex 복구 계약은 SSE 종료 이벤트에 있으므로 비스트리밍까지 성공 상태로 감싸는 것은 피하는 편이 낫습니다.

    너의 추천
    스트리밍 Responses 전용 helper를 만들고, 로컬 입력 허용 거절과 최종 upstream 413에서 bounded/redacted message를 가진 단 하나의 response.failed 이벤트를 HTTP 200 SSE로 반환하세요. error와 last_error 모두 type을 invalid_request_error, code를 context_length_exceeded로 맞추고, 그 뒤 data: [DONE]으로 닫으세요. 테스트는 로컬 preflight 413, 직접 passthrough upstream 413, 번역 adapter 413, Anthropic 이미지 1회 축소 후 재실패, combo에서 추가 후보 미전송, 비스트리밍 413 유지, 관련 없는 4xx/5xx의 상태·본문 보존을 각각 고정하는 것이 좋습니다. 이 범위라면 #2511을 닫지 않고도 지금의 “대화가 계속 재연결되며 막힘” 버그를 작게 해결할 수 있습니다.

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

    Translated Message

    Original language: Korean

    Review · Priority 74 / 80

    This issue requests that when a provider rejects a request for exceeding input size limits, OpenCodex should stop retrying the same large request and instead convert it to a "context full" end signal that Codex can understand. Currently, when the first response ends with HTTP 413 before streaming begins (as in the attached example), Codex treats it as a generic unexpected HTTP error and attempts 5/5 reconnections. Users experience complete conversation blockage because when they send new messages, the same large context is reattached.

    dev currently has checkInputAdmission, which preemptively blocks if the input estimate significantly exceeds the model's known context limit. However, this path directly returns an HTTP 413 in JSON form from src/server/responses/core.ts. The path that receives 413 after actually sending a request to the provider also safely reads and cleans the error body before returning the same HTTP status. In other words, the determination of "too large" exists, but there is still no protocol signal that the streaming Responses client can use to decide next steps.

    The critical point is not about silently truncating requests, but about accurately conveying the meaning of the error. If OpenCodex silently discards user sentences or images, users cannot know what wasn't delivered to the model. Conversely, if only streaming requests emit a response.failed termination event with context_length_exceeded code, Codex can use its own history and compression policies to organize the next turn and stop retrying the same large request.

    This is superficially similar to #2511 but is not a duplicate. #2511 is an optional feature to set per-provider byte budgets and pre-shrink or remove images, while this issue is about converting actual 413s (including long text, not just images) into a failure signal that Codex understands. Therefore, #3170 should be maintained separately, and #2511 should be connected later as an optional pre-shrinking feature.

    Also, not all 413s should be unconditionally treated the same way. There are 413s unrelated to Responses input context, such as admin API upload limits or hub relay limits. The conversion location should be narrowed to the Responses data boundary, and conversion should only occur when the client has requested streaming and it's confirmed by either local input admission check or actual provider request size rejection. Error messages must pass through existing redactSecretString and length limits to prevent request fragments or tokens returned by the provider from leaking to the client.

    src/server/responses/core.ts:3092-3106 - Local checkInputAdmission rejection also ends with generic JSON HTTP 413 for streaming requests, so it doesn't enter Codex's context overflow handling path.
    src/server/responses/core.ts:5949-5965 - Anthropic image requests already retry with a smaller image step; don't remove this safe single recovery, but should emit the context overflow termination signal only after that retry also fails with 413.
    src/server/responses/core.ts:5968-6037 - This is a common path that consolidates all abnormal provider responses to HTTP errors, so if you don't branch only confirmed size overflow for streaming Responses first, retrying the same large request will continue.
    src/bridge.ts:2015-2038 - formatErrorResponse is a function shared by multiple generic HTTP errors, so changing the format of all 413s here would affect other APIs. It's safer to have a dedicated, narrow-scope response.failed generator.
    src/combos/failover.ts:151-172 - context_length_exceeded is already structured to not pass to the next candidate and stop. The new termination signal must also maintain this structured code to prevent the same oversized input from being repeatedly sent to different combo targets.

    Points requiring maintainer judgment:

    • Decide whether local input_admission_refused should also appear as context_length_exceeded for streaming Codex requests, or whether only actual provider 413s should be converted. Both user symptoms and reconnection prevention seem to require the same signal.
    • When confirming provider 413s, decide whether to look only at HTTP status or require known request_too_large codes or body classification. Too strict risks symptoms remaining due to provider-specific wording differences; too loose risks misclassifying 413s unrelated to Responses.
    • Decide whether to maintain existing HTTP 413 JSON for non-streaming requests. Since the Codex recovery contract is in SSE termination events, wrapping even non-streaming with success status should be avoided.

    Our recommendation:
    Create a streaming Responses-only helper and return a single response.failed event with bounded/redacted message as HTTP 200 SSE from both local input admission rejection and final upstream 413. Set both error and last_error type to invalid_request_error and code to context_length_exceeded, then close with data: [DONE]. Testing should fix each of: local preflight 413, direct passthrough upstream 413, translation adapter 413, Anthropic image retry-once then fail, no additional candidates sent in combo, non-streaming 413 preserved, and unrelated 4xx/5xx status/body preserved. With this scope, you can address the current "conversation repeatedly reconnects and blocks" bug without closing #2511.

    This comment was written by grok-bot

  6. DamnUi commented on Sep 1, 2026

    @DamnUi
    ContributorAuthor

    Also something similar is happening with 408

    Image

    im not sure why switching to sol 5.6 fixed it but had to alternate

  7. DamnUi commented on Sep 1, 2026

    @DamnUi
    ContributorAuthor

    Also an example of kimi not working as there were no images in this chat and kimi failed as soon as i switched to it

  8. Ingwannu commented on Sep 1, 2026

    @Ingwannu
    Owner

    Draft fix is now available in #3177 at exact head 6d5eea6a163106232242af14335f69f34ca6cad5.

    The regression coverage confirms:

    • passthrough and translated streaming 413s become one terminal response.failed with context_length_exceeded;
    • local input admission performs no upstream I/O;
    • the existing Anthropic image-tightening retry still runs exactly once before terminal failure;
    • combo routing does not send the same oversized request to another target;
    • non-streaming 413 and unrelated 400/503 status/body behavior remain unchanged;
    • the provider body marker is absent from the streaming client failure.

    Current validation is typecheck green, focused 6/6, translator-budget contract 1/1, import-connected suite 2,887 pass / 4 skip / 0 fail, and docs build 409 pages. The PR remains Draft until full exact-head CI and the remaining local full-suite gate are reviewed; it has not been merged.

  9. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    Fixed by #3177, landed on dev as 0d6424f.

    A streaming Responses 413 now converts into one terminal response.failed carrying context_length_exceeded, so Codex stops its 5/5 reconnect loop and can apply next-turn compaction instead. Coverage spans local input admission, direct passthrough, translated adapters, exhausted Anthropic image tightening, and the outer combo boundary; non-streaming 413 status and body fidelity are preserved, and the failure message is proxy-owned so an upstream 413 body cannot echo request content.

    Closing manually because pull requests target dev rather than the default branch.

  10. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    Landed via #3177 at 0d6424f

    이 이슈의 수정은 이미 dev에 들어왔습니다. squash 커밋에 Closes가 없어서 GitHub가 이슈를 자동으로 닫지 않았습니다.

  11. Ingwannu commented on Sep 1, 2026

    @Ingwannu
    Owner

    Thanks — the 408 screenshot may look similar in Codex, but it is not safe to fold it into the confirmed 413 fix yet.

    HTTP 413 is explicit input-size rejection, so mapping it to context_length_exceeded is semantically correct and stops the reconnect loop. HTTP 408 normally means the upstream timed out waiting for or processing the request. A timeout can be transient, model/load-specific, or transport-specific; converting every 408 into “context full” would make Codex compact or stop retrying for the wrong reason. Switching to Sol succeeding is also consistent with a model-specific timeout, not proof that the request exceeded context.

    #3177 therefore remains deliberately scoped to confirmed Responses 413 paths. For the 408, please attach the sanitized OpenCodex request-log entry for one failed attempt (provider/adapter, routed model, HTTP status, upstream error code/message, attempt count, and request ID; no tokens or credentials). If the same request reliably returns the same structured 408, we can trace whether it should be classified as a terminal timeout, a bounded transient retry, or a provider-specific size signal in a separate focused issue.

  12. DamnUi commented on Sep 1, 2026

    @DamnUi
    ContributorAuthor

    understood yeah i cant reproduce 408 may just be traffic on the model/ provider.

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

    bugSomething isn't workingenhancementNew feature or requeststreamingSSE, WebSocket, terminal stream frames

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions