Skip to content

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
NousResearch:mainfrom
jessepollak:fix/native-image-payload-limits
Draft

fix(agent): constrain image payloads and enforce a request-body budget for the ChatGPT Codex transport#79877
jessepollak wants to merge 2 commits into
NousResearch:mainfrom
jessepollak:fix/native-image-payload-limits

Conversation

@jessepollak

@jessepollak jessepollak commented Aug 6, 2026

Copy link
Copy Markdown

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-looking APIConnectionError — and because sessions run store=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_output items).

Two commits:

  1. Constrain native image payloads before embedding — inbound images are re-encoded in memory (EXIF-oriented, JPEG/PNG, quality-then-dimension reduction) to fit a configurable data-URL byte cap before entering conversation history; bmp/tiff transcode to PNG; unidentifiable images are skipped with a warning instead of poisoning the session. Config: agent.native_image_max_payload_bytes / agent.native_image_max_dimension.
  2. Enforce a total request-body budget at codex preflightagent/request_budget.py measures 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

  • Bisected the live endpoint to locate the ceiling (12 probes: clean pass/fail flip between 1,175,000 and 1,200,000 bytes).
  • Replayed a real failed 7.05MB request dump through the budget: constrained to 931KB in 1.4s and accepted live (HTTP 200) by the same endpoint that had silently dropped it.
  • 62 unit tests for the new modules + the transport suite (239) green on this branch.
  • An independent correctness review (GPT-5.6 agent with repo access) produced 5 findings with concrete reproductions — a size-keyed retry memo that could substitute an unrelated equal-length request (memo removed), aggregate under-cap images bypassing the last resort, untouched 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 in tests/agent/test_request_budget.py.

Notes for reviewers

  • The budget deliberately lives at transport preflight, not the compression layer: with 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).
  • There are now parallel image-fitting implementations (agent/image_payloads.py vs tools/vision_tools.py/conversation_compression.py); consolidating them is worthwhile but was kept out of this PR to keep the diff reviewable.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openai OpenAI / Codex Responses API area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 6, 2026
@jessepollak
jessepollak force-pushed the fix/native-image-payload-limits branch from 8927a5d to f28ab9c Compare August 6, 2026 22:43
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
jessepollak force-pushed the fix/native-image-payload-limits branch from f28ab9c to 15746ca Compare August 6, 2026 23:38
@aerialdreamxl

Copy link
Copy Markdown

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

errors.log

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

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/openai OpenAI / Codex Responses API sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants