fix(agent): constrain image payloads and enforce a request-body budget for the ChatGPT Codex transport - #79877
Draft
jessepollak wants to merge 2 commits into
Draft
Conversation
jessepollak
force-pushed
the
fix/native-image-payload-limits
branch
from
August 6, 2026 22:43
8927a5d to
f28ab9c
Compare
The chatgpt.com/backend-api/codex transport hard-drops request bodies over ~1.2MB without an HTTP response (measured 2026-08-02 by bisection: <=~1,175,000 bytes returns 200; >=~1,200,000 is dropped after ~15s), surfacing as a retryable-looking APIConnectionError that permanently wedges the session, since store=false re-uploads the same history every call. Token-based compression cannot protect this limit: images and large tool outputs are byte-dense but token-cheap. Adds agent/request_budget.py, enforced at codex preflight (runs on retries too): truncate history tool outputs largest-first, re-encode embedded images, placeholder history images, truncate active-tail outputs, and as a last resort placeholder any remaining oversized image. User text is never touched; under-budget bodies pass through untouched. Verified against a real failed 7.05MB request dump: constrained to 931KB and accepted live (HTTP 200). Also lowers the per-image native payload default 900KB -> 500KB: against a ~1.17MB total envelope, 900KB per image leaves no headroom for instructions plus tool schemas plus history. Co-Authored-By: Claude <noreply@anthropic.com>
jessepollak
force-pushed
the
fix/native-image-payload-limits
branch
from
August 6, 2026 23:38
f28ab9c to
15746ca
Compare
|
Nearly same issue here on Aliyun Dashscope Token Plan registration, here's error.log, saying "An existing connection was forcibly closed by the remote host" when trying to continue long sessions |
19 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The ChatGPT Codex transport (
chatgpt.com/backend-api/codex/responses) hard-drops oversized request bodies: the server reads the whole upload, then closes the connection without sending any response. Measured by bisection (2026-08-02): bodies up to ~1,175,000 bytes return HTTP 200; ~1,200,000 and above are dropped after ~15s. The client can only surface this as a retryable-lookingAPIConnectionError— and because sessions runstore=False, the identical oversized history is re-sent on every call and every retry, permanently wedging the session. One unresized phone photo (11MB JPEG → ~15MB base64 data URL) is enough.Token-based context compression cannot protect this limit: images and large tool outputs are byte-dense but token-cheap, so a body can be 6x over the wire ceiling while token accounting reports plenty of headroom (observed: 7.05MB body at ~170k tokens, 98% of it replayed
function_call_outputitems).Two commits:
agent.native_image_max_payload_bytes/agent.native_image_max_dimension.agent/request_budget.pymeasures the serialized body just before send (including retries) and, when over budget, degrades a deep copy via a ladder: truncate history tool outputs largest-first (the most recent tool exchanges stay verbatim) → re-encode embedded images → placeholder history images → truncate active-tail outputs → last-resort placeholder any remaining oversized image. User text is never modified; under-budget bodies pass through untouched. Progress is tracked with exact per-item serialization deltas (2 full dumps per over-budget call). Gated on the codex issuer classification so other Harmony-speaking backends are never silently degraded. Config:agent.max_request_body_bytes(default 1,000,000 — the measured ceiling minus headroom).Related issues: #27478 (pre-resize inbound images in gateway), #47339 (413 — context compression doesn't evict image/vision payloads), and the same failure class in OpenAI's own clients (openai/codex#13508, #7682, #2908 — there it surfaces as a clean 413; through this client it's a connection drop).
Verification
function_call.arguments/reasoning.encrypted_content, first-pass truncation floors exhausting on 200-output histories, and a chars-vs-bytes marker label. All five are fixed and covered by regression tests intests/agent/test_request_budget.py.Notes for reviewers
store=False+ client retries, enforcement must be per-request. A follow-up could make the shrink durable in stored history (today the degradation is recomputed per call).agent/image_payloads.pyvstools/vision_tools.py/conversation_compression.py); consolidating them is worthwhile but was kept out of this PR to keep the diff reviewable.