Repository navigation
Inline image output costs ~230 tokens but 4.2 MB on the wire, so token-based context management never sees the payload that wedges the thread #41338
Description
Related issues
I searched first. This overlaps #18629 (inline base64 tool images poisoning threads) and #31579 (thread stuck after a failed auto-compact), and I have 👍'd both.
I am filing separately because I have a measurement that contradicts a stated hypothesis in #18629 and that neither issue reports: the images are nearly free in tokens and enormous on the wire. #18629 says the payload is "likely inflating token usage." Measured here, it does not — and that asymmetry looks like the actual reason nothing in the client notices before the thread dies. Happy for this to be folded into #18629 if you'd rather keep it in one place.
New here: the per-image token cost, a 34.78 MB payload (32× #31579's 1.08 MB), and view_image on a local file as the producer rather than a browser/screenshot plugin.
What version of the Codex App are you using?
Codex App 26.810.52044; codex-cli 0.150.1
What subscription do you have?
Not specified here. Observable from the session's own telemetry: limit_id: "codex", a 10,080-minute (weekly) primary window at 81% used, credits.balance: "0".
What platform is your computer?
Linux 7.0.0-30-generic x86_64 x86_64
What issue are you seeing?
A thread became permanently unable to continue. Three consecutive user turns each returned:
{"detail":"Bad Request"}Immediately before that, three pre-turn auto-compactions failed:
16:14:50 WARN remote compaction v2 stream failed; retrying request after delay retries=1 max_retries=2
compact_error=stream disconnected before completion: failed to send websocket request: Connection closed normally
16:14:52 WARN remote compaction v2 stream failed; retrying request after delay retries=2 max_retries=2
compact_error=stream disconnected before completion: websocket closed by server before response.completed
16:14:54 WARN falling back to HTTP
16:14:54 DEBUG Compressed request body with zstd pre_compression_bytes=34777589 post_compression_bytes=492064
16:14:55 ERROR session_task.run:run_turn: Failed to run pre-sampling compact
After the compaction failed, the turn proceeded on the uncompacted history: five websocket retries, all closed by server before response.completed, HTTP fallback, then Turn error: {"detail":"Bad Request"}. Every later message repeated it, with the body growing slightly each time as the failed turns appended (34,777,589 → 34,779,905).
The measurement
The request body was 34.78 MB. Of that, eight view_image outputs were 4,200,348 bytes each:
response_item type |
Count | Bytes | Share |
|---|---|---|---|
custom_tool_call_output |
226 | 35,243,778 | 96.3% |
reasoning |
281 | 771,319 | 2.1% |
custom_tool_call |
226 | 375,302 | 1.0% |
message |
56 | 150,834 | 0.4% |
| everything else | 4 | 48,958 | 0.1% |
The eight images alone are 33.6 MB, 92% of the entire request.
Now the part I think matters. Taking last_token_usage.input_tokens on the turn immediately after each image entered history:
| Image | Bytes on the wire | Prompt-token delta |
|---|---|---|
| 1 | 4,200,348 | +239 |
| 2 | 4,200,348 | +238 |
| 3 | 4,200,348 | +230 |
| 4 | 4,200,348 | +232 |
| 5 | 4,200,348 | +227 |
| 6 | 4,200,348 | +216 |
| 7 | 4,200,347 | +235 |
| 8 | 4,200,346 | +226 |
| total | 33.6 MB | 1,843 |
1,843 tokens — 0.71% of the 258,400 context window — for 92% of the request body. For comparison, ordinary exec outputs in the same session cost +18,516, +11,000 and +7,240 tokens each while contributing almost nothing to the byte count.
Why this looks like the root cause
Context management is token-denominated; the transport constraint is byte-denominated. Only one of those is tracked.
- The client cannot see the problem. Its own accounting said 233,226 / 258,400 — a normal 90%, the routine trigger for compaction — while the actual body was 34.78 MB.
pre_compression_bytesis computed and logged immediately before the POST and feeds no decision. - Compaction cannot fix it, even when it works. A token-budget compactor has no incentive to evict images: they are the cheapest items per token and the most expensive per byte. It will evict the 18,516-token text output and keep the 4.2 MB picture. A textbook-perfect compaction could leave the body essentially unchanged.
- Compaction cannot run at all. It is itself a model request carrying the same payload, so when the payload is what fails, the only shrink mechanism fails with it — then the turn proceeds uncompacted anyway. That converts one bad turn into a permanently dead thread.
- The error says nothing.
{"detail":"Bad Request"}gives no size, no token count, no offending item, no remedy.
What steps can reproduce the bug?
- Session id:
01a04764-a97d-71c2-ace0-6fcf5816cc42 - Model:
gpt-5.6-sol,reasoning_effort=max - Context window: 258,400. Last successful turn: 233,226 input tokens (90.3%).
- Two earlier auto-compactions in the same session succeeded — both before any image entered history.
- Run a long session with ordinary tool use until it sits near the auto-compact threshold.
- Have a tool write a large PNG to disk. Ours was 1280×820 at 3,149,928 bytes.
- Inline it:
const r = await tools.view_image({path, detail:"original"}); image(r.image_url);— repeat ~8 times. - Send another message. Pre-turn auto-compact fires, fails, and every turn from then on returns
{"detail":"Bad Request"}.
The image producer does not matter — #18629 collects browser-use, Playwright, image_gen and plugin cases. What matters is an unbounded artifact reaching history via base64.
What is the expected behavior?
- Bound the inlined artifact. A 1280×820 frame tiles to the same image tokens whether the file is 13 KB or 3.1 MB — the measurement above shows the extra bytes buy the model nothing. Downscale or re-encode to a byte ceiling before the item enters history. This is lossless in every sense that matters and removes ~99% of the payload on its own.
- Track bytes as a budget, not only tokens. The number is already computed at
pre_compression_bytes. Evicting by bytes-per-token would have dropped eight items worth 1,843 tokens and reclaimed 33.6 MB. - Give compaction a local fallback, and never proceed uncompacted. If the compaction request fails, elide the heaviest items client-side and retry without the network. Running the turn anyway on the uncompacted history is what makes the state permanent rather than transient.
- Report the real reason. A 400 on a 34.78 MB body should say so.
Additional information
detail: "original"was requested; the stored item recordsdetail: "high". Nothing downscaled or capped it either way.- On the producer side: the oversized PNG came from a local MCP server we maintain, which was writing PNGs as stored deflate blocks — 3,149,928 bytes for a line drawing that compresses to 16,687. We have fixed that (299× smaller, byte-identical pixel data), so this particular trigger is gone for us. It only removes one producer, though: nothing client-side stops the next large image from any source.
- We depend on these images and are not going to stop sending them. The text alternatives our tooling exposes — a 400×200 character grid, a 32-entry colour ranking, single-pixel reads — answer "what colour is at this point", not "what does this drawing show". The right fix is bounding the payload, not rationing the capability.
- On whether this is a size limit: I want to be careful. Thread gets stuck after failed auto-compact; app-server keeps stale in-memory history until unload #31579 hit the same 400 at 1,084,457 bytes, ~32× smaller than ours, alongside a dangling
No tool output found for tool search call, so the rejection may be structural rather than a byte cap. What I can state from this session is the payload composition, the token/byte asymmetry, and that the client had no recovery path. I could not observe the server's reason. - Possible recovery, from Thread gets stuck after failed auto-compact; app-server keeps stale in-memory history until unload #31579: archiving and immediately unarchiving the thread moved it from
systemErrortonotLoaded, unloading the stale in-memory history, after which the same prompt succeeded. Worth documenting if confirmed — right now a user's only visible option is to abandon the thread.
Activity
github-actions commentedon Aug 28, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
MR-Wayne commentedon Aug 31, 2026
I can reproduce a closely related failure on Windows with a single local image. In this environment, the same unbounded inline-image hazard manifests as a hard token-limit rejection rather than only a transport-level 400.
Environment
26.825.6671.00.151.0-alpha.7.2gpt-5.6-solthrough a custom Responses-compatible provider272,000tokensSequence and measurements
2012x938,1,174,733bytes.tools.view_image({ path, detail: "original" })input_imagecontaining adata:image/png;base64,...URL, roughly 1.57 MB inline.27,153input tokens.Your input exceeds the context window of this model. (prompt is too long: 498792 tokens > 272000 maximum) (trace: t22ffb61nrl)A forked subagent inheriting the same history failed similarly at
497,711tokens.Why this seems relevant
This looks like the same client-side hazard reported here, with an additional failure mode: a Responses-compatible provider may account for or serialize the inline data URL as prompt text. A single tool result can therefore jump from about 27k to about 499k tokens, completely skipping the auto-compaction threshold. At that point, no recovery or compaction request can be assembled within the model window.
The provider is custom, so its image-token accounting may differ from OpenAI's service. However, the unbounded Base64 artifact is still produced and retained by the Codex client, and there is no local guardrail before the next request.
Expected safeguards
view_imageresult.No image, local path, session ID, provider URL, or credential is included in this report.
a18846013774-blip commentedon Sep 1, 2026
I can reproduce the same byte-vs-token asymmetry on the current macOS desktop build, with an additional failure mode: compaction succeeds and the thread remains usable, but every subsequent turn takes 2–3 minutes before the first reasoning item because the compacted history still resends all retained image data URLs.
Environment
26.825.515110.151.0-alpha.7.2gpt-5.6-sol,reasoning_effort=maxcompaction_image_budget,remote_compaction_v2,enable_request_compressionCompaction still retains the full image bytes
Across five successful compactions, the compacted record grew as follows:
The latest
replacement_historycontains 134 items and 23 completedata:image/png;base64,...values totaling 57,737,086 characters. These are not summaries or file references.This happened after the app had already updated to the bundled version above, with
compaction_image_budget=true.Low token count after compaction, but ~140 s to first reasoning
Immediately after successful compaction:
So the UI/token accounting says the context is small, but the wire payload remains very large.
Same machine/network/model comparison
Local history parsing/compression was not the multi-minute bottleneck: the long-thread request was dispatched about 1.6 seconds after submission. Nearly all of the delay occurred after the 28.8 MB compressed request was sent and before response headers arrived.
A later call still had 357,632 / 361,443 input tokens cached, but the serialized request was about 62.9 MB (30.8 MB after zstd) and HTTP 200 still took about 153 seconds. Prompt caching therefore does not mitigate repeatedly transmitting/deserializing the inline image bytes.
Expected safeguard
In addition to token budgeting:
pre_compression_bytesis already measured.This is the slow-but-not-hard-failed version of the issue: the server accepts the payload, but the thread becomes practically unusable despite successful compaction and a low post-compaction token count.
No session ID, repository content, local paths, images, credentials, or raw logs are included here.
huntario commentedon Sep 6, 2026
Three ordinary view_image calls produced ~2.74 MB of persisted inline image data in a 3.0 MB rollout. Subsequent requests replayed all three input_image tool outputs with reasoning.context: "all_turns", eventually producing 413 Failed to buffer the request body. Removing those image objects reduced the rollout to ~438 KB.
Workaround (remove images from conversation history)
codex resume --lastReproduce:
Results in this in the request
Then Codex stores the result as:
Basically... just don't use screenshots or your conversation will be corrupted and unusable.
!!! AGI IN 2 WEEKS!!!! !!!
pang-cream commentedon Sep 6, 2026
I can confirm that this issue is still reproducible on codex 0.153.4.
I inspected one affected Codex Desktop thread using a custom Responses provider over HTTP. The thread had been successfully compacted earlier, and immediately after that compaction there were no inline images left in the active reconstructed history. However, after subsequent
view_imagecalls, the image payload accumulated again.Measurements from the affected thread:
The important detail is that this is not one image being duplicated many times inside the same request.
These are 20 different images produced by earlier
view_imagecalls. The problem is that old image payloads remain in active conversation history and continue to be carried by later model requests, including turns where no new image is added and the old images are no longer relevant.In this thread, image history grew approximately like this after the last successful compaction:
After reaching ~25.65 MB, later model calls continued to retain those images.
A manual
/compacthas been an effective workaround in this thread: previous successful compactions removed the inline images from the reconstructed active history. But the images start accumulating again as soon as moreview_imagecalls are made, so this does not appear to fix the underlying issue.For accuracy: the 25.65 MB image size and 26.47 MB active-history size are measurements reconstructed from the local session history, not packet-captured HTTP Content-Length values. The 20+ MB per-request size is what I observed separately at the gateway, so I do not want to claim the reconstructed size is an exact wire-size measurement.
I have also seen some 502/520/send/retry failures in the affected session, but I cannot establish that all of those failures were caused by the retained images.
This seems to reinforce the core issue described here: image payload size is largely invisible to token-based context management, while the same inline Base64 data can remain expensive at the transport layer across subsequent requests.
Ideally, historical
view_imageresults should either be stored/referenced out-of-line, aggressively byte-bounded, or removed from active request history once they are no longer needed, rather than repeatedly serializing the full Base64 payload.I can provide additional sanitized measurements if useful. I can also submit
/feedbackfrom the affected thread for internal diagnostics without posting the raw session file, image Base64, provider URL, or credentials publicly.xuanmingguo commentedon Sep 8, 2026
Additional measurements and an isolated reproduction of the error-classification gap on Linux, with Codex 0.151.0 and 0.153.4.
Separating the causes
Our initial incident included a bug in our own custom Responses proxy: a compatibility transform duplicated tool-result images. That duplication was not a Codex bug, and we fixed it separately. Nevertheless, the byte/token mismatch and inability to recover from an ordinary HTTP 413 remain relevant here.
After removing duplication without changing image bytes or quality, a real request containing 14 synthetic tool images was 29,803,115 serialized bytes, while reported input usage was only 11,741 tokens. The model correctly read the image marker and the stream ended with
response.completed. Thus low token usage did not imply a small HTTP body. The gateway in our setup reported a 52,428,800-byte limit; this is an observed provider-specific limit, not a proposed universal Codex limit.Reproduction independent of any model or credentials
Using an isolated
CODEX_HOME, a localhost mock Responses HTTP server, andcodex exec --ephemeral --jsonwith a custom provider (wire_api = "responses",requires_openai_auth = false, HTTP/stream retries set to zero), submit a short text prompt. Return one of these responses forPOST /v1/responses:HTTP 413,
Content-Type: text/plain:Request body too large. Maximum size is 52428800 bytesHTTP 400,
Content-Type: application/json:{"error":{"type":"invalid_request_error","code":"context_length_exceeded","message":"Synthetic context limit for local reproduction."}}HTTP 200,
Content-Type: text/event-stream, followed by a blank line:Both installed versions produced the same results, with one request per case:
unexpected status 413 Payload Too Large: ...response.failedwith the same codeCodex ran out of room in the model's context window...All three end the current turn. The mock test establishes classification, not successful compaction. Separately, with the resident 0.151.0 App Server and a real custom provider, translating a known body-budget rejection into the third form caused
contextWindowExceeded; the next turn performed nativecontextCompaction, completed with a retained text marker, and subsequently usedview_imagesuccessfully. This was not same-turn recovery and did not retain every historical image in active context.Current source and possible fix direction
Static inspection of main at
6fee98cc85a69ee44a856121cac0f64505cf48d0still shows the distinction between HTTP error mapping and SSE context-error recognition. This is source inspection, not a runtime test of that commit.It would help to recognize structured HTTP context-limit errors and provide bounded, byte-aware recovery for request-size rejections. HTTP 413 need not mean token-window exhaustion: recovery should avoid retrying an identical oversized body, ensure the compaction request itself fits, and preserve current image inputs or explain when they require splitting. A provider-specific serialized-body budget should be separate from token accounting. Related recovery discussion: #7808.
Submitting these measurements here under the current contribution policy rather than opening a duplicate issue or external PR. Only synthetic image measurements and sanitized errors are included.
mycyg commentedon Sep 8, 2026
Additional measurements on Codex core 0.153.4 / macOS arm64, using the built-in OpenAI provider with ChatGPT authentication. This case adds a large compressed request payload, repeated compaction retries, and a small WebP encoding comparison.
Environment and workload
26.901.51231; bundled core:0.153.4.gpt-6-astra, reasoning effortxhigh.remote_compaction_v2,compaction_image_budget, and request compression were already enabled.openaiprovider, before subsequent timeout and image-preprocessing experiments.Measurements
Across the recorded conversation, 97 distinct images entered as original response items: 85 from tool outputs and 12 from user messages. This count excludes duplicated event records and historical compaction snapshots.
At the failing compaction, reconstructed active history contained 53 images, with approximately 32.86 MB of image data URLs. The compaction request was 33.58 MB before zstd and 24.39 MB after zstd. The history measurement strongly suggests images account for most of the request size; I did not capture and independently parse the wire request.
Sanitized request-compression measurements for the initial attempt and its two retries:
Local zstd compression was fast. The long delay was in the remote request lifecycle:
The whole compaction cycle lasted 18m42s before interruption, with no successful compaction record. The final attempt was interrupted, not a third confirmed network failure.
The same error recurred with
retries=2 max_retries=2. Each retry reconstructed essentially the same large payload; no meaningful reduction in request bytes was observed.A separate affected conversation sent a 29.27 MB raw / 21.48 MB zstd compaction request and, after approximately 15m08s, received:
One important distinction: an earlier successful compaction in the main case did reduce the replacement history to 15 items containing one user image. Subsequent image inspection then grew the active image count back to 53. This case is therefore about image accumulation within the next window and failure to complete the next compaction, rather than proof that every successful compaction retains every old image.
Reproduction shape and uncertainty
The observed workflow is: run a long visual task, repeatedly inspect generated pages/screenshots, accumulate dozens of distinct tool images, and allow mid-turn auto-compaction to trigger. I do not yet have a standalone deterministic reproducer.
There were confirmed proxy/connection failures earlier in one run, including WebSocket failure followed by HTTP fallback. I also experience similar compaction stalls when ordinary network use appears normal, but that does not establish that the long-lived inference connection is healthy. These measurements do not prove a universal byte limit or identify which network hop/server terminated every stream. They do show that the recovery path repeatedly carries a substantial image payload.
Proposed improvement
WebP feasibility measurement
Five images sampled across the affected active history, at unchanged dimensions:
The lossless outputs passed decoded-pixel equality checks. The lossy outputs were measured for size, not validated for task-specific readability. These are five-image results, not a benchmark of the entire request or proof that re-encoding fixes the disconnects. Same-dimension re-encoding primarily addresses upload bytes; visual token usage should not be assumed to fall proportionally.
FengYeeLx commentedon Sep 9, 2026
Related reports with additional current-stable evidence
For cross-reference, I have two related reports from Codex CLI 0.153.4 / Windows:
#43015— Severe CLI reliability failure: 63.8 MB image-history requests before any compaction, WebSocket fallback, and prolonged stalls on Windows #43015Directly related to this issue: a real image-assisted session accumulated a 63.8 MB serialized request before compression / 45.85 MB after zstd, with 61.28 MB of retained inline image data before any compaction. It adds a pre-compaction active-history case using the built-in OpenAI provider and ChatGPT authentication.
#43022— Severe WS stalls in 0.153.4: live Ping/Pong still trips the idle watchdog (Windows loopback-only reproduction) #43022A separate but potentially amplifying reliability issue: a deterministic localhost reproduction shows that a Responses WebSocket can remain protocol-live with valid Ping/Pong activity while the application-event idle watchdog still expires and triggers fallback.
I am linking both here because large retained image history can make full-request retry/fallback paths substantially more expensive, while #43022 demonstrates that WebSocket liveness/retry behavior has an independent failure mode. I am not claiming that the two issues have the same root cause.
jroth1111 commentedon Sep 11, 2026
Additional recovery evidence: a deterministic localhost mock reproducer using the real Codex CLI, plus an important compatibility constraint for offline repair of newer rollouts.
Reproduction and measured recovery
Tested separately with codex-cli 0.154.0 and 0.152.1 on macOS. Each test used an isolated temporary
CODEX_HOME, a custom Responses provider bound only to localhost, no credentials, and entirely generated images. This was not a request to the production OpenAI gateway.function_call/function_call_outputrecords containing 12 distinct valid 1280×1280 noise PNGs asinput_imagedata URLs. Preserve consecutive ordinals for paginated rollouts. Each encoded image is below 10 MiB.Encoded request body exceeds 67108864 bytes.compactedrecovery checkpoint. Itsreplacement_historypreserves all active response items and call/output IDs, replacing ten older image objects withinput_textarchive references while retaining the two most recent images. Preserve associated harness metadata and consecutive ordinals. No model summarization is performed.Both versions produced these measured outbound JSON sizes:
The successful final response came from the mock. This establishes that the real client can replay the bounded checkpoint and complete a follow-up request; it does not establish production-gateway recovery, model reasoning quality, or the production cap's exact accounting.
A separate affected local rollout had 117,965,114 bytes of reconstructed active response-item JSON and 79 inline image occurrences. That is an offline history estimate, not a captured request. It contained only one compaction checkpoint, so deleting stale checkpoints would not address that particular active-history payload.
Why repair should be native
Blindly shrinking existing JSONL records is unsafe for newer paginated storage. On source commit
fc948f8c473e5d11e780ffcf1fd7f812a2020932, thread-history materialization resumes from stored byte offsets, while model-context loading reconstructs paginated lineage and replacement checkpoints. The tested approach leaves every original byte/ordinal unchanged and adds a recovery suffix. It does not reduce the on-disk rollout size.The local repair implementation passed 19 unit tests covering unchanged history prefixes, metadata/ordinals, backup and extracted-image hashes, recent-image retention, malformed input, concurrent changes, and refusal to edit running clients. It declines unsupported lineage/rollback/advanced-history cases rather than guessing. A file not appearing in
lsofis insufficient by itself: an app-server can still hold cached history in memory.An official byte-aware request preflight and offline recovery operation would be valuable, covering both inference and compaction, preserving recent visual inputs and retrievable originals, and avoiding repeated submission of the same deterministically oversized body. Related: #30441 and #43015.
Only synthetic test results, aggregate measurements, and public source references are included here. No real thread IDs, local paths, prompts, original images, raw rollouts, account data, provider credentials, or private logs are attached.
Update: recurrence during continued work, and a recovery copy that leaves the source untouched
The affected long-running Goals workflow became unusable again after continued image review. The saved active history had accumulated 64 new inline image occurrences, totaling 118,506,921 bytes of reconstructed active response-item JSON. These are offline history measurements, not a captured wire payload.
The local workaround's limitation matters: its background repair waited for Codex/IDE clients to exit, so it repeatedly deferred while the workload remained active. Offline repair is recovery, not prevention of an active request-size failure. This limitation belongs to the external workaround; it is not a claim that Codex ships that maintenance behavior. Enabling the already-enabled compression and compaction-image-budget flags did not add an aggregate active-request byte guard.
The installed CLI had been upgraded to 0.154.0, but that does not upgrade already-running processes or the desktop app's separately bundled core. I am therefore not presenting the recurrence as a clean 0.154.0 regression test.
A revised recovery path now snapshots the source, checks that it did not change during preparation, and publishes a separate self-contained conversation under a new identity with a bounded replacement checkpoint. It does not edit the loaded source conversation or assume that a closed file descriptor means the app has unloaded it. Existing response items, call/output pairs and metadata are retained; selected older image objects become explicit references to preserved originals.
For the affected conversation:
The separate, entirely synthetic CLI 0.154.0 + localhost 64 MiB mock test also passed for this recovery-copy approach: 78,732,011 bytes / 12 images rejected → 13,175,417 bytes / 2 images accepted, with ten archive references and the source rollout unchanged. The local repair utility passed 24 unit tests, including recovery while other clients exist, refusal to publish a copy that still exceeds its target, metadata/ordinal preservation, unchanged source bytes, and live scanning despite in-place repair being deferred. These utility tests are not upstream Codex integration-suite results.
Requested native behavior remains:
No private IDs, paths, images, prompts, credentials, account identifiers or raw logs are included in this update.
htbk commentedon Sep 14, 2026
Additional report with an explicit 64 MiB rejection, repeated across three image-heavy desktop tasks, including a clean continuation that failed again. Posting at the account owner's request after preserving the work and inspecting local evidence.
Environment inspected September 14, 2026
com.openai.codex, version 26.908.40834, build 8881 (read from the installed bundle).Actual failure
{"error":"decompressed request body exceeds 67108864 bytes"}The task becomes unable to process subsequent short prompts. Continuing from a concise handoff in a fresh task initially works, but the same failure recurs as native image results accumulate. A third image-heavy task on the same installation also failed. For that task, the owner reported that manual compaction failed or was unavailable. A fresh continuation from saved files completed successfully.
Measurements
A read-only streaming audit of two preserved JSONL histories found:
The native image payload alone exceeds 67,108,864 bytes in each case. The replacement's largest single native tool-result record was 15,907,967 bytes; another was 11,780,403 bytes. This does not depend on a user pasting base64 as message text.
Method: count active
response_item/turn_contextserialized records after the latest compaction, including retained replacement history, and count native inline image fields. Exclude duplicate UIevent_msgcopies. These are local history estimates, not captured HTTP request lengths; the HTTP envelope, system instructions and tool definitions are not included. Total JSONL file size is reported separately and is not asserted to be the outgoing body size. Both histories parsed without malformed records.Observed workflow and impact
Existing files were recoverable. Generated originals and useful work stranded outside the main project needed explicit recovery. This is loss of task usability, not a claim that the service deleted local files. I have not intentionally reproduced it again after installing a workaround.
Requested safeguards
Our current workaround is a conservative 16 MiB local active-history budget, an 8 MiB reserve before a native image-generation result, and viewing-only JPEG derivatives capped at 384 KiB (plus native crops when needed). Production originals remain unchanged. The guard has already stopped further generation before the failure threshold. This is a workflow workaround, not a fix to Codex.
No private document content, images, conversation transcripts, credentials or private paths are included in this report.
HankHuang0516 commentedon Sep 23, 2026
A fresh recurrence is reproducible on the current Codex desktop build.
Sanitized diagnostics:
This confirms the issue persists in a newer desktop/bundled CLI build. The missing guardrail is still byte-aware request preflight plus bounded/deduplicated image persistence. A supported recovery action that creates a text-only continuation from the latest compaction would also prevent users from having to edit live rollout files.
bickov commentedon Sep 26, 2026
Same blind spot shows up on the Anthropic side too, just from the other end. instead of the token counter missing the bytes, claude code enforces a hard pixel gate before token accounting is even involved. the exact error there is: "An image in the conversation exceeds the dimension limit for many-image requests (2000px). Start a new session with fewer images." so on that stack the failure is a wall, not a silent accumulation, but the underlying issue is the same, image size is decoupled from whatever the client's context management is actually watching.
One thing about the idea of sending a preview or bounded crop instead of the raw image, further up in the thread. that only helps if the preview is actually cheaper in tokens, and that's not automatic. I measured a downscaled screenshot against a structured text extraction of the same page. on a single screen the extraction runs a bit cheaper, something like 1,000 tokens against 1,600, nothing dramatic. on a tall page it can go the other way, extraction ran to about 11k tokens against under 500 for the image in one test I ran. so whatever preflight check gets built here should probably measure both candidates rather than assume text or a resized crop is automatically the cheap option.
chipfighter commentedon Sep 28, 2026
Similar here on the Windows desktop app. I was iterating on image generation with GPT in one long task, and after a dozen or so images the connection kept dropping and the retries every few minutes never went through. Every request was carrying every image from the task's history again, about 44 MB each. The only way out was a new task, which loses the conversation.
While this is open, I built a workaround and open-sourced it (disclosure: I'm the author): Codex Attachment Manager, a plugin for the Codex desktop app.
need IMG-xxxand you check it again.Limits: desktop app only; default OpenAI backend only (it won't install over your own
openai_base_url); results of Codex's built-in image generation can't be unchecked. Tested on Windows with Codex 26.924. macOS and Linux only pass CI so far, so reports are welcome.Install it from Codex's Plugins page (add the marketplace
chipfighter/codex-attachment-manager) or with a one-line command; see the README.fix(runtime): DeepSeek 请求经本地代理压缩旧图,修复读图多的对话 413 卡死