Repository navigation
Local compaction v2 retains unbounded input_image payloads, causing repeated auto-compaction #33493
Description
What version of Codex is running?
- Codex Desktop:
26.707.72221 - Embedded CLI:
codex-cli 0.144.2 - Platform: macOS 26.3 (Build 25D125), Apple Silicon
- Model:
gpt-5.6-sol
What issue are you seeing?
A long-running, image-heavy Codex Desktop thread enters a repeated auto-compaction cycle. After a compaction completes, the resulting history remains only slightly below the auto-compaction threshold. Even a short new question pushes it over the threshold again, so almost every new turn starts with another compaction.
The compacted replacement_history keeps historical input_image items, including their full inline base64/data-URL payloads. Repeated compactions keep copying the same images into new compacted records.
This produces two related effects:
- The post-compaction prompt remains close to the context threshold, causing immediate re-compaction on subsequent turns.
- The rollout JSONL grows rapidly because the same inline image payloads are persisted repeatedly.
Local evidence
From one affected thread:
| Metric | Observed value |
|---|---|
| Rollout JSONL size | approximately 4.16 GB |
| Compaction records | 224 |
| Images retained by the latest compacted replacement history | 92 |
| Retained inline image/base64 characters | approximately 26.47 million |
| Retained text characters | approximately 188,000 |
| Effective context window reported for the session | 258,400 tokens |
| Default auto-compaction threshold | approximately 244,800 tokens |
| Prompt size immediately after compaction | approximately 236,000-237,000 tokens |
The post-compaction history therefore has only about 8,000 tokens of headroom. A normal follow-up turn is sufficient to trigger compaction again.
No full rollout is attached because it contains private prompts, paths, tool output, and images. I can provide additional redacted structural statistics if needed.
Steps to reproduce
- Use a long-running Codex Desktop thread containing multiple pasted screenshots or image inputs.
- Continue until local/remote-v2 compaction occurs.
- Inspect the newest
type: "compacted"rollout record. - Confirm that
payload.replacement_historyretains user messages containinginput_imagedata URLs. - Send a short follow-up question.
- Observe another automatic compaction even though the previous compaction just completed.
- Repeat several turns and observe the rollout file growing as image payloads are copied into additional compacted records.
Expected behavior
After compaction:
- Retained multimodal history should be bounded by a realistic image/token/byte budget.
- There should be enough hysteresis below the auto-compaction threshold for ordinary follow-up turns.
- Old inline image payloads should not be duplicated indefinitely in every compacted rollout record.
Actual behavior
The current retained-message text budget treats images as zero text tokens and preserves them:
ContentItem::InputImage { .. } => 0,and later:
ContentItem::InputImage { .. } => truncated_content.push(content_item),The message-level .max(1) floor limits the number of image-only messages only very weakly. It does not account for image cost inside mixed text/image messages, image count, encoded byte size, or vision-token cost.
Current source:
codex-rs/core/src/compact_remote_v2.rsmessage_text_token_counttruncate_message_text_to_token_budgettruncate_retained_messages_for_remote_compaction
Possible fix direction
For active compacted history:
- Give retained images a conservative estimated token cost, using the existing image-size/detail estimator where possible.
- Add explicit caps for retained image count and/or encoded bytes.
- Keep the newest images first and replace older images with a lightweight placeholder once the image budget is exhausted.
- Add a regression test with many mixed text/image messages verifying that compacted history is bounded and a short next turn does not immediately trigger compaction again.
For rollout persistence, consider storing images as content-addressed blobs or references rather than repeating full base64 strings. That storage concern may be better handled separately under the existing image-heavy rollout issue.
Related issues
- Codex remote compaction deadlocks when input_image payloads remain in compacted replacement history #24388: same underlying retained-
input_imageproblem, closed after the compaction architecture changed; this report reproduces the behavior on the newer local compaction path. - Responses WebSocket falls back when compacted replacement_history contains large inline images #24550: large inline images in compacted
replacement_historycause Responses WebSocket fallback. - Codex Desktop hangs when opening an image-heavy session rollout (~183 MB, huge inline image_url records), and the thread becomes hard to recover from the sidebar #24676: image-heavy rollout JSONL causes Codex Desktop hangs and difficult session recovery.
- Backtrack rewind sets context remaining to 0% and triggers auto-compaction #9601: images can poison context estimation after history mutation.
I am willing to help with additional redacted diagnostics, test-case design, or an implementation if maintainers agree on the approach and invite a PR under the repository contribution policy.
Activity
github-actions commentedon Jul 16, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
ignatremizov commentedon Jul 19, 2026
Additional current reproduction and source trace (2026-07-19).
Reproduction
I inspected another long-lived image-heavy thread after a local compaction-v2 checkpoint. Sanitized structural measurements from the latest
compacted.payload.replacement_history:input_imagepartshighgpt-5.6-sol)The token sequence makes the impact visible:
total_tokens=294,141,input_tokens=0, which rendered as 17% context remaining.input_tokens=342,406,output_tokens=230,total_tokens=342,636, which rendered as 3% remaining.The apparent drop was not caused by the file-read output displayed immediately before the second token event. That usage belongs to the request that generated the read call, so the result could not yet have been part of its input. The first server-provided usage corrected the lower-bound local estimate for the already-installed image-heavy prompt.
No raw rollout is attached because it contains private prompts, paths, tool output, and images. The counts above were computed structurally without printing content.
All 142 retained images in this reproduction were already surrounded by the canonical local-image text wrapper: an opening
<image ... path="...">item immediately before theInputImageand a closing</image>immediately after it. The compactor can therefore remove the inline bytes without losing the model-callable source path.Current upstream source analysis
Current upstream
mainnow uses a 64k retained-message budget, but the budget remains text-only:codex/codex-rs/core/src/compact_remote_v2.rs
Lines 52 to 54 in 0fb559f
build_v2_compacted_historyselects retained user/developer/system messages from the compaction prompt and passes them through the retained-message truncator:codex/codex-rs/core/src/compact_remote_v2.rs
Lines 443 to 460 in 0fb559f
The truncator charges only text.
InputImageandInputAudiocontribute zero, and the partial-message path always preserves them:codex/codex-rs/core/src/compact_remote_v2.rs
Lines 482 to 560 in 0fb559f
The current tests explicitly lock in this behavior by asserting that images survive truncation and that an image-only message costs only the
.max(1)message floor:codex/codex-rs/core/src/compact_remote_v2.rs
Lines 729 to 819 in 0fb559f
Local-image prompt construction already emits the source path in an adjacent model-visible wrapper and keeps the raw image as a separate content item:
codex/codex-rs/protocol/src/models.rs
Lines 1392 to 1400 in 0fb559f
codex/codex-rs/protocol/src/models.rs
Lines 1568 to 1588 in 0fb559f
view_imagealready accepts a model-generated path plus an optional environment ID and resolves it through the selected environment filesystem:codex/codex-rs/core/src/tools/handlers/view_image.rs
Lines 53 to 57 in 0fb559f
codex/codex-rs/core/src/tools/handlers/view_image.rs
Lines 118 to 168 in 0fb559f
There is already a content-aware image estimator elsewhere in core. It replaces inline base64 payload size with an estimated model-visible image cost (approximately 1,844 tokens for ordinary resized images, with separate original-detail patch accounting):
codex/codex-rs/core/src/context_manager/history.rs
Lines 521 to 579 in 0fb559f
codex/codex-rs/core/src/context_manager/history.rs
Lines 652 to 679 in 0fb559f
That estimator is used after history replacement for the provisional token-count event, but not for retained-message selection.
recompute_token_usagealso estimates only base instructions plus installed history and marks the result asinput_tokens=0, explaining why the first real server count can jump materially:codex/codex-rs/core/src/session/mod.rs
Lines 3668 to 3704 in 0fb559f
Historic rollouts currently receive no equivalent cleanup. Reconstruction selects the latest surviving
replacement_historyas its base and installs it exactly before replaying the suffix:codex/codex-rs/core/src/session/rollout_reconstruction.rs
Lines 113 to 185 in 0fb559f
codex/codex-rs/core/src/session/rollout_reconstruction.rs
Lines 317 to 345 in 0fb559f
Both resume and fork use the shared reconstruction path. It currently prepares legacy media in memory while explicitly leaving the recorded rollout unchanged, so image-heavy checkpoints remain active and are processed again on every reconstruction:
codex/codex-rs/core/src/session/mod.rs
Lines 1248 to 1333 in 0fb559f
codex/codex-rs/core/src/session/mod.rs
Lines 1345 to 1370 in 0fb559f
Proposed implementation / fix spec
I suggest landing the smallest coherent safety fix first: raw media bytes should not cross a successful compaction boundary, but reopenable source references should. The compaction output is already the semantic handoff for the prior window.
InputImageandInputAudioparts before retained-message budgeting.<image ... path="...">,InputImage,</image>sequence, preserve the existing path wrapper and replace onlyInputImagewith bounded text such asImage bytes removed after compaction; use view_image with the path above if needed.build_v2_compacted_historybeforetruncate_retained_messages_for_remote_compaction.reconstruct_history_from_rolloutalready distinguishes the latest basereplacement_historyfrom the newer rollout suffix.run_remote_compact_v2_attemptfor already-running sessions upgraded in place or histories that bypass cold reconstruction.for_prompt()constructs the compaction request:codex/codex-rs/core/src/compact_remote_v2_attempt.rs
Lines 41 to 85 in 0fb559f
ResponseItem::Compaction/ContextCompactionboundary.CompactedItem.replacement_historysnapshots.omitted_image_count,omitted_audio_count, omitted inline-media bytes, repair-applied/version, vacuum bytes before/after, and vacuum failure status. Keepretained_image_countfor verifying that the installed replacement reaches zero under this policy.view_imagepaths remain strings at the tool boundary, but resolution should continue through the selected environment and its filesystem.environment_id.Acceptance criteria
replacement_historyafter compaction contains no rawInputImageorInputAudiopayloads.view_imagewith a retained path and successfully reload the image when that source remains available.environment_id; a host-only path is never treated as an executor path.InputAudioreceives the same treatment so the parallel zero-cost path does not become the next instance of this bug.This would address both the immediate context death spiral here and the repeated checkpoint amplification tracked in #23257 without requiring a new persisted-history representation.
dorukardahan commentedon Jul 23, 2026
i can confirm this on another affected Codex 0.145.0 session.
i used a read-only structural scan. i did not inspect or upload prompts, paths, images, tool output, project names, or thread IDs.
After one successful compaction, the installed replacement history still contained 32 unique PNG
input_imageitems. All haddetail: high. The inline payloads used about 18.8 million base64 characters. The existing estimator in Codex maps these images to about 59,000 model-visible image tokens.The first real server usage after that compaction reported 104,988 input tokens and 13,056 cached input tokens. This is consistent with a compacted checkpoint that still keeps a large image floor.
Current
mainat808d3c2702ce8eae007c457aa930e7c3b68dd5f6still gives images and audio zero cost during retained-message selection and then keeps them:codex/codex-rs/core/src/compact_remote_v2.rs
Lines 439 to 556 in 808d3c2
The live history is then prepared with
for_prompt()before each model request:codex/codex-rs/core/src/session/turn.rs
Lines 292 to 299 in 808d3c2
The same codebase estimates an ordinary resized image as about 1,844 tokens:
codex/codex-rs/core/src/context_manager/history.rs
Lines 493 to 506 in 808d3c2
This does not prove an exact image-only token cost because usage events do not include a modality split. It does confirm a mismatch between retained-message selection and the model-visible estimator.
i agree with the invariant proposed above. Raw media from the compacted window should not enter the installed replacement history. A bounded reference or unavailable marker can remain, while the original media stays available to the transcript. i am not posting the rollout because it contains private data.
If maintainers want legacy repair and physical vacuum tracked separately, i can open a focused follow-up issue.
romainsimon commentedon Jul 23, 2026
Adding a corroborating data point for this on macOS, because the impact went beyond disk and into a very large token bill.
Environment
What happened
One image-heavy thread (lots of generated images over a long session) grew past 1 GB. From there it matches the mechanism described in this issue exactly: the inline image data gets re-serialized on compaction/resume, so the session is re-written in full again and again instead of referencing prior image content.
On-disk evidence (
~/.codex/sessions)The disk (460 GB) filled to 0 bytes twice in one day, making the machine unusable (writes failing with ENOSPC system-wide). Multiple Codex renderer processes sat at 70 to 75% CPU while this happened.
Token / cost impact (this is the part I'd flag)
Because each resume re-feeds the entire ~1 GB session as context, usage tracking (ccusage) reported roughly 2.68 billion input tokens and ~73 billion cached input tokens for Codex/gpt-5 in a single day, estimated at about $35,700 for that one day!!! Non-Codex usage on the same machine that day was under $100.
I suspect this is #33735 (giant sessions from duplicated inline images) compounding with #22411 (app-server eager-loading all session files on every
thread/list): once each session is multiple GB, repeatedly re-reading them all turns the disk bloat into a massive background token burn as well. So the two issues together produce a much worse outcome than either alone.Suggested fixes (echoing and extending this thread)
replacement_historyrecords.Workaround
Fully quit the ChatGPT/Codex app (so no session files are held open), then delete the redundant oversized rollout copies from
~/.codex/sessionswhile preserving the normal small threads. That reclaimed ~50 GB in my case.bbingz commentedon Jul 25, 2026
Analysis (community)
Residual analysis for #33493 (compaction v2 retains unbounded
input_imagepayloads → repeated auto-compaction) under docs/contributing.md — invitation-only. No unsolicited PR.Root-cause hypothesis
The images are only the trigger. The reason the cycle is relentless rather than occasional is a missing fixed-point guarantee in the compaction loop: nothing ever checks whether the post-compaction history actually fits below the auto-compact threshold, so any retained payload (images counted at 0 tokens, an uncapped chained summary) keeps the prompt at ~97% of the limit and re-fires compaction on every subsequent turn — while each fire appends another full
replacement_historysnapshot to an append-only rollout with no GC. Verified at HEAD4c43465133:1. Images are counted as 0 tokens and budget-exempt — exactly as reported. In the v2 truncation pass,
message_text_token_countmapsContentItem::InputImageandInputAudioto0(codex-rs/core/src/compact_remote_v2.rs:505-525), andtruncate_retained_messages_for_remote_compaction(compact_remote_v2.rs:478-502) only ever truncates text viatruncate_message_text_to_token_budget— an image-only message sails through the 64kRETAINED_MESSAGE_TOKEN_BUDGET(:56) untouched. The local path has no image budget at all. Your 92 retained images / 26.47M base64 chars in the newest compacted record is this code path.2. No post-compaction fits-check → no fixed point. After compaction,
recompute_token_usageonly re-estimates (compact.rs:384); nothing compares the result againstauto_compact_token_limit()(=context_window * 9/10,protocol/src/openai_models.rs:459-470). Pre-turn compaction fires on every turn whentoken_limit_reached(session/turn.rs:846-872), and the mid-turn rollover path carries the dev comment "as long as compaction works well in getting us way below the token limit, we shouldn't worry about being in an infinite loop" (turn.rs:394). With ~8k headroom after compaction (your measurement), one normal follow-up turn crosses 90% again — the loop has no exit condition. There is also no re-fire guard: nothing records "just compacted, usage unchanged" (state/session.rshas no such marker).3. The retained summary is uncapped and chained. Each compaction builds replacement history from ≤20k tokens of recent user messages (
COMPACT_USER_MESSAGE_MAX_TOKENS,compact.rs:56) plusSUMMARY_PREFIX + summary_suffixwhere the suffix is the last assistant message of the compaction turn (compact.rs:347-353) — i.e. the prior summary re-summarized, appended with no size cap inbuild_compacted_history(compact.rs:671-684). Text alone can therefore hold the history near threshold across compactions even after the images are fixed.4. Every compaction appends a full snapshot; the chain is never pruned.
replace_compacted_historypersistsRolloutItem::Compactedcarrying the completereplacement_history, plus fullWorldStateandTurnContextitems (session/mod.rs:3168-3215), into an append-only writer. Reconstruction consumes only the newest surviving checkpoint (session/rollout_reconstruction.rs:156-186— the reverse scan takes the firstreplacement_historyit meets and replays only the suffix). Every older snapshot is provably dead weight, and there is no GC/retention pass anywhere inrollout/orcore/. Your 4.16 GB / 224 compaction records — and the closed #24191's 892 MB / 62 records — are this mechanic; image payloads just make each record ~100x larger.5. The overflow safety valve is itself unbounded. On
ContextWindowExceededthe local path removes ONE oldest history item, resetsretries = 0, and re-issues a full remote stream call (compact.rs:309-324) — an item-count-bounded retry loop with no constant cap; the remote path treats it as non-retryable (protocol/src/error.rs:358-396), which is the terminalcontext_length_exceededsibling of this issue.Ranked fix outline (all OSS, codex-rs):
input_imageat a real token estimate (or a conservative per-image constant) inmessage_text_token_count, and lettruncate_retained_messages_for_remote_compactiondrop/placeholder oldest images when over budget — closes the reported trigger inside the code the v2 fix introduced.recompute_token_usage, if usage is still ≥ the auto-compact limit, escalate deterministically (drop injected initial context → halve retained user-message budget → cap summary) and, failing that, emit an actionable terminal error instead of silently re-arming the same loop. This replaces theturn.rs:394assumption with a structural guarantee.build_compacted_history.Compacted, a best-effort pruner (temp-file + rename, modeled on the existing cold-compression worker inrollout/src/compression.rs) keeping SessionMeta + newest checkpoint + suffix bounds on-disk growth to O(window) instead of O(total tokens). Largest diff; also relieves the rollout-size half of this issue.MAX_TRIM_PASSESand bail with an actionable error before reaching 1 item.Fail-first tests I'd want before any merge: (1) a retained history with 26M chars of
input_imageand <1k tokens of text is truncated by the v2 budget pass (today: passes through intact); (2) after a compaction whose recomputed usage is still ≥ threshold, the next turn does NOT immediately re-fire an identical PreTurn compaction (today: it does); (3) K compactions leave exactly 1 liveCompactedsnapshot plus suffix in the rollout (today: K full snapshots). Happy to draft the test patch if a maintainer wants it.Related: #32888 is the stale-token-usage sibling on the same resume/trigger axis (resume seeds live usage from the last persisted
TokenCount,session/mod.rs:1340-1345); #33235 was auto-flagged as a possible duplicate. No unsolicited PR from me.fujiwarakasei commentedon Jul 29, 2026
Adding a sanitized reproduction from a newer macOS/App/CLI combination. This corroborates the existing diagnosis and quantifies the on-disk amplification from repeated inline images.
Environment
26.721.41059(build5848)cli_version 0.145.0-alpha.18codex-cli 0.146.026.5.2 (25F84), Apple Silicongpt-5.6-solSanitized structural measurements
The affected rollout was inspected with a streaming structural analyzer. No prompts, screenshots, paths, credentials, or raw records are attached.
compactedrecordscompactedrecordsdata:imagereferencesThe largest single
compactedrecord was 39,309,704 bytes. It contained 69 inline image references totaling 39,140,726 bytes; 68 were unique within that checkpoint. Several later checkpoints had effectively the same inline-image byte total, showing that the same retained media was serialized again into successivereplacement_historysnapshots.The 331 exact unique image data URLs represent about 83.1 MiB of decoded image bytes, but repeated embedding plus base64 expansion turned them into roughly 1.74 GiB of text in the rollout. Removing image strings from the accounting leaves only about 31 MiB for all ordinary JSON, text, and metadata.
This confirms that the abnormal growth is not ordinary conversation text. It is dominated by repeated full
input_imagedata URLs in append-only compaction checkpoints, and the behavior persists on the version combination above.The raw rollout is private and will not be uploaded, but I can provide additional redacted structural counters if maintainers need a specific measurement.
24 remaining items
Klohto commentedon Aug 18, 2026
I can reproduce this on the current macOS desktop build, with slow compaction as the main symptom.
Environment
26.810.520440.147.026.5(25F71)gpt-5.6-sol, high reasoning01a01430-d0a2-7c03-a278-a1d7d201e696Redacted diagnostic evidence
2026-08-18T17:14:33Zand completed at17:17:39Z: 3 minutes 6 seconds.run_auto_compact{reason=ContextLimit phase=MidTurn}.replacement_historyis 5,219,050 serialized bytes with 57 retained items.input_imagevalues containing 5,022,440 inline image/data-URL characters.No task text, screenshots, raw rollout, tool output, local paths, or credentials are attached.
This corroborates the reported cause. Compaction preserves full historical
input_imagepayloads, so later compactions repeatedly process a multi-megabyte multimodal history. In this case, the request succeeds but the latency has become several minutes.kevindh19 commentedon Aug 19, 2026
Additional current macOS reproduction: rapid compaction loop
I reproduced the same retained-
input_imagefailure on a separate current Codex Desktop task.Environment
0.148.0-alpha.1526.5.1, Apple Silicon258,400tokensPrivacy-safe structural diagnostics
No prompts, screenshots, raw rollout data, private project content, local paths, credentials, or task identifiers are included.
This independently corroborates both reported effects: compacted history retains the full historical image set instead of bounding or externalizing it, and every new checkpoint duplicates those image payloads into the append-only rollout. The resulting task remained technically active but became operationally unusable because compaction no longer created meaningful headroom.
I can provide the privacy-safe
jqmeasurement commands or help design a regression fixture if maintainers want them, and I would be willing to implement a scoped fix if invited under the repository contribution policy.bbingz commentedon Aug 20, 2026
Analysis (community)
@kevindh19 — thanks for the current macOS Desktop reproduction (embedded CLI
0.148.0-alpha.15). That still matches the residual I traced earlier on #33493.Root-cause hypothesis (re-checked 2026-08-20 at
main3b45c2906): remote compaction v2 still treats inlineinput_imageas zero tokens for the retained-history budget, then keeps the image bytes when truncating text. So a successful compact does not create a fixed point: the next turn can still be over budget and compact again.Live path:
message_text_token_countcountsInputText/OutputTextonly;ContentItem::InputImageandInputAudiocontribute 0 (codex-rs/core/src/compact_remote_v2.rs:634-647).truncate_message_text_to_token_budgettruncates text againstremaining, but theInputImage/InputAudioarm unconditionally pushes the content item (:689-691) — images are never dropped by the token budget.retained_input_image_countexists (:545-552) as a metric, not as a drop/GC gate.That is why a current Desktop task can still enter a rapid compaction loop after images are in
replacement_history: compact shrinks text, images stay, usage stays high, compact fires again.This is distinct from #33235 (image inherit on fork into model context). Same payload class, different copy event (compaction retain vs fork inherit).
No unsolicited PR from me. If maintainers want a fail-first test, the existing
build_v2_compacted_history_counts_retained_input_images(:914) is the right fixture to extend: after compact, retained images must either count toward the budget or be stripped.Happy to refine the HEAD pin if a newer compact path landed after
3b45c2906.markmdev commentedon Aug 21, 2026
Current macOS build still reproduces this, with per-turn latency
This is still happening on the current installed Codex Desktop build. It is not only damage left behind by an older build.
Environment:
26.818.22352(build6872)0.148.0-alpha.2126.6.1(25G76), Apple Silicon2026-08-19with embedded CLI0.148.0-alpha.15The app updated and restarted on August 20. After that restart, the affected task produced two more compacted records, at
21:52:58Zand23:21:09Z. Each retained the same nineinput_imageitems insidepayload.replacement_history, totaling16,755,582inline image characters. This confirms that the current build is still creating the bad state.A privacy-safe structural scan of the task now shows:
205,621,888bytes5,0187input_imagenodes retained across completed replacement histories:4063,405,544characters43(126,975,157characters), left untouched by the scanThe user-visible symptom is latency on every turn, not only during compaction. New tasks on the same installation respond in roughly 1–2 seconds, while this task takes 40+ seconds for trivial prompts. A recent short acknowledgement took
68.493s; another ordinary turn took195.155s.A schema-aware dry run found that the affected task can be repaired by replacing only
input_imagenodes inside completedpayload.replacement_history, while preserving current/uncompacted images and backing up the rollout. No cleanup was applied before taking these measurements.The product needs both parts:
I am not attaching the rollout because it contains private prompts, screenshots, local paths, and tool output.
laffo16 commentedon Aug 21, 2026
I have a second reproduction matching this issue, this time in Codex CLI on Windows.
Environment
gpt-5.6-solmaxauto258,40001a01907-f597-7d91-a8c1-e7cb0ce7d5d00.148.0/statusreport CLI0.149.0for the same resumed session. The rollout does not record the client version for every resume.User-visible behavior
The TUI repeatedly displayed
Context compactedwithout completing useful work:Workingafter 6m33s.Workingafter 6m42s.Conversation interrupted./statusthen showed0% left (285K used / 258K)around another compaction.Privacy-safe rollout measurements
These measurements inspect record structure, counts and sizes only. I am not attaching the raw rollout because it contains private prompts, paths, tool output and images.
Measured at compaction 60 on
2026-08-21T16:21:45Z:replacement_historyinput_imagenodesimage/pngThis is not mainly duplicate images within a single history: the latest history has 131 image nodes representing 130 exact payloads. The major amplification is the same retained images being copied into each subsequent compacted record.
Rapid compaction bursts
Two separate loops occurred:
There were zero function / tool calls between the compactions in either burst. Individual intervals were often only about 32-72 seconds.
The nearest token telemetry around compaction 42 changed from
226,678before compaction to295,687after it. Subsequent post-compaction totals remained around279K-296K.For compaction 60:
299,346299,414258,400Across the rollout, 31 of 60 first post-compaction token samples were already at or above the full
258,400context window. This explains why compaction immediately re-armed without a meaningful new turn.The
/statusUI and rollout telemetry use somewhat different totals (285Kversus299,414in the latest observation), but both independently place the session above the258Kwindow immediately after compaction.Multi-agent context
The run spawned seven subagents and contained substantial collaboration activity before the loop. That may have accelerated history growth, but the actual rapid-compaction windows contained no agent or other tool calls, so multi-agent activity does not appear to be the immediate loop mechanism.
The rollout also contains six
turn_abortedevents overall, and the TUI displayedConversation interruptedduring the later burst.Interpretation
This appears to reproduce the mechanism described in this issue:
input_imagedata URLs.The separate reasoning-accounting issue in #39767 may influence when the first compaction is triggered on
gpt-5.6-sol, but it cannot account for the retained 12.5 million image characters or the repeated copying of 130 image payloads. This rollout therefore provides independent evidence for the retained-image problem reported here.I can provide additional redacted structural measurements if maintainers need them.
garpunzel commentedon Aug 21, 2026
Additional current macOS reproduction: gradual image accumulation causes per-turn compaction thrash
Privacy-safe structural scan of another affected Codex Desktop rollout. This case accumulated screenshots over a long-running thread rather than attaching one large batch.
Environment
0.148.0-alpha.9gpt-5.6-sol,xhigh353,400tokensStructural measurements
18input_imagenodes in latest replacement history:2949,162,318124items, approximately49,367,092serialized characters17and18retained the exact same ordered image payload setcontext_compactedevents occurred5m46sapart inside one unfinished turnToken sequence
247,329.309,488 / 353,400input tokens.330,134input tokens.268,560.309,619input tokens.The second compaction therefore produced only about
20.5Knet prompt reduction relative to the last pre-compaction call. The retained image floor immediately restored the prompt to roughly87.6%of the effective window.No rollback or retry duplicated the UI event: the rollout records distinct compaction window IDs and incremented
window_numberfrom17to18.This matches the reported mechanism: raw historical images survive the retained-message budget, compaction removes mostly text/tool history, and the next ordinary turn immediately approaches the threshold again. User-visible result is several minutes of compaction latency per question and an operationally unusable task.
No raw rollout, prompts, paths, images, tool outputs, or thread IDs are included.
ccomaeng commentedon Aug 22, 2026
Independent macOS/Desktop reproduction on a current build, with two separate rollouts reaching 25+ GB.
Environment
26.818.31338(build6892)codex-cli 0.146.026.6.2(25G83)gpt-5.6-solopenai/codex@4f39251a010a8bd7d692d25fb33832ff06f1635aPrivacy-safe structural evidence
Affected rollout A:
28,091,832,834bytescompactionrecords:261context_compactedevents:261input_imageoccurrences:14,74314,741108,795,437bytes before compaction and80,273,208bytes after compaction.581,308tokens against an auto-compaction limit of244,800.token_limit_reached=true, repeatedly followed byrun_auto_compactwithreason=ContextLimitandphase=MidTurn.Affected rollout B, from a separate earlier image-heavy ebook task on the same machine:
26,467,249,845bytes37,991,677bytes and 51 token-limit events around245k-251ktokens against the same244,800limit.ContextLimit/MidTurnauto-compaction pattern.No raw rollout, prompts, paths, images, or base64 payloads are attached because they contain private data.
Current source trace
Current
mainstill matches the observed failure mode:codex-rs/core/src/compact_remote_v2.rs::message_text_token_countchargesInputImageandInputAudioas zero text tokens.truncate_message_text_to_token_budgetpreserves those media content items.build_v2_compacted_historyretains user messages and reports the retained image count.codex-rs/core/src/session/turn.rsruns mid-turn auto-compaction and immediatelycontinues the turn. There is no local consecutive-attempt ceiling or check that the compacted context actually fell materially below the threshold before another pass.This makes the failure self-amplifying: images survive compaction, the resulting context stays over the limit, another full image-bearing request/history is persisted, and the append-only rollout grows roughly by the repeated payload size on each pass.
Suggested safety fix
This also corroborates the storage amplification reported in #33735, but the non-decreasing mid-turn compaction loop appears to be the immediate trigger for the two 25+ GB files above.
This issue or PR was generated by LazyCodex.
Tag: lazycodex-generated
iwasrobbed commentedon Aug 23, 2026
FYI Tibo finally posted about this bug: https://x.com/thsottiaux/status/2091407991736332689?s=20
garpunzel commentedon Aug 23, 2026
“Dude, we're like on TV right now!”
Nurozen commentedon Sep 27, 2026
Adding a data point from codex-cli 0.157.0 with
compaction_image_budgetenabled, since themeasurements in this thread predate that flag becoming default-on (#40994, 2026-08-26).
codex features liston the affected install:The budget is on and the ratchet still happens.
Measurements
One thread, 12 compactions, measuring
payload.replacement_historyon eachcompactedrollout record:Text stays flat at 0.07-0.10 MB across all 12. Base64 goes 1.25 → 4.61 → 5.82 MB and never
decreases. Effective floor on every request in the thread became 5.82 MB that compaction could
not reduce.
Where the bytes sit
The summary itself is not the problem. In window 12,
replacement_historyis 41 items: 37 usermessages, 3 developer messages, and one
{"type":"compaction","id":"cmp_...","encrypted_content":...}holding the actual summary. All 5.82 MB is in the retained user messages, as
input_imagecontent items with inline
data:image/png;base64,...URIs at"detail":"high".Distribution in that window:
So
truncate_retained_messagesis keeping every image in the retained set rather than droppingthem under the image budget.
Resizing is working, format is not
resize_all_imagesis doing its job: a source attachment of 2852x1892 reached the wire at1928x1280. But all seven images go up as PNG. Re-encoding those same seven at JPEG q85:
Worth noting as a mitigation, though it only moves the ceiling rather than removing it, since the
retention behavior is unchanged.
Related
#41338 explains why the budget doesn't catch this from the model's side: an inline image is ~230
tokens but megabytes on the wire, so anything reasoning in tokens is blind to it. The
ImageReference/ file-id work in #45543 and #45794 looks like the structural fix.
Happy to supply the redacted rollout records if useful.
H234598 commentedon Oct 7, 2026
Additional extreme persisted-history data point from Linux. For the figures below, I extracted only structural fields, sizes, timestamps, token counters, and hashes; no prompt text, image contents, local paths, or thread identifier are included.
Version caveat
The affected rollout was created with
codex-cli 0.147.0and accumulated across later resumes. I inspected it with the current embeddedcodex-cli 0.161.0, where:Therefore, the figures below are strong evidence for the historical persistence-amplification and compaction fixed-point problem, but they should not be read as a claim that all 1,700 records were generated by 0.161.0. A reopen attempt under 0.161.0 surfaced
Context compacted, then the turn was aborted after about 38 seconds and no new successfulcompacteditem was written. Because that attempt did not complete, it does not establish whether the current image budget can recover this persisted history.Full-rollout structural scan
compactedrecordscompactedrecordsThe latest
replacement_historycontains 530 items: 528 user messages, one developer message, and one compaction item. It retains the same five images, whose inline strings total 4,620,786 characters. The finalcompactedJSONL record alone is 5,030,887 bytes. Across the last 24 records, record size remains 4,989,798–5,031,815 bytes (median 5,026,940), so compaction is not converging toward a materially smaller persisted state.Fixed-point failure: local estimate versus next real request
For compaction windows 1677–1700:
token_countevent reportedlast_token_usage.input_tokens=0and estimated only 43,503–53,432 total tokens.The measurements establish a repeated estimator/request mismatch plus severe persistence amplification. The likely mechanism is:
Read-only integrity check
I left the affected thread closed and collected 160 samples over 904 seconds at 5–9-second intervals. Rollout size and mtime did not change, and no sample found an open file descriptor to it. This confirms the measurement process did not resume or further inflate the affected session; it does not claim continuous FD observation.
This data point suggests two regression checks: one for newly generated histories with
compaction_image_budgetenabled, and one for legacy persisted histories created before or across that mitigation:Is migration/recovery of already-persisted image-bearing replacement histories intended to be covered by
compaction_image_budget, or does that require a separate recovery path?nathanchillz commentedon Oct 7, 2026
Additional macOS reproduction on a newer desktop build, with measured request sizes and long WebSocket stalls. Related symptom: #33094.
Environment: macOS 26.5.1, 24 GB RAM; desktop 26.1002.52244 (13536); bundled CLI 0.162.0-alpha.2; VS Code extension openai.chatgpt 26.1002.51308; model gpt-6-astra, reasoning effort xhigh. Observed on 2026-10-07.
After two successful manual compactions, the affected desktop chat retained 31 inline input_image items. Their encoded strings totaled 60,209,084 characters, almost all of the 60,259,416-byte serialized replacement history.
A minimal diagnostic request took 46.426 seconds in the affected chat, versus 5.784 seconds in a fresh chat with the same model and effort. Working directories differed, so this is not a fully controlled comparison. Local tool execution in the old chat took approximately 71.7 ms.
The old-chat test made two HTTP requests of 44,504,214 and 44,505,354 bytes after compression. Compression-to-HTTP-200 intervals were 20.503 and 19.998 seconds. These timings include upload and server wait; their respective contributions are not isolated. An earlier compaction had logged WebSocket failures and a fallback to HTTP. Separate VS Code requests measured approximately 23.26 MB compressed each.
After restarting the desktop app, another minimal diagnostic request remained unanswered for 370.172 seconds, until manually interrupted. It produced no assistant response or tool call. Five retries logged: "websocket closed by server before response.completed". A subsequent WebSocket connection succeeded, but no response arrived before cancellation. No HTTP fallback was logged during this final turn.
The user reports the same symptoms over Wi-Fi and a mobile hotspot. An independent upload test measured 16.1 Mbit/s. This can contribute to large HTTP request latency, but does not by itself explain the repeated WebSocket failures. Updates, restarts and compactions did not resolve the issue. The exact cause of the connection closures remains unconfirmed.
A Feedback submission from the affected chat was successfully uploaded. Its session ID has been provided privately through OpenAI Support. No private prompts, images, local paths, credentials or session identifiers are included in this public comment.
Please investigate whether this reproduction shares the retained-image mechanism described here, and provide a supported repair or migration for existing chats that preserves their history and image references.
nathanchillz commentedon Oct 7, 2026
Follow-up: VS Code comparison isolates repeated connection failures on a trivial diagnostic turn.
On 2026-10-08 (Europe/Paris), I ran the same minimal diagnostic request in an affected existing chat and then in a fresh chat, approximately 3 minutes apart. Both used the same project directory, gpt-6-astra, reasoning effort xhigh, and the same running VS Code Codex process (verified from the transport logs). The submitted text matched after trimming surrounding whitespace.
The request only asked for a short acknowledgement, one terminal call to list the current directory and its immediate entries, and a short final acknowledgement. No file contents were read or changed, and no images were attached, generated, or requested in either test turn.
The existing chat repeatedly logged
websocket closed by server before response.completed. After falling back to HTTP, it prepared two compressed request bodies of 23,272,677 and 23,272,280 bytes. The fresh chat completed over WebSocket without observed reconnects or fallback. Message timings above are transcript recording times, not exact UI first-token measurements; body sizes are logged compressed bodies, not an independent packet capture.This demonstrates a transport/retry failure mode before a trivial local operation, beyond simply waiting for image generation or a complex coding task. Existing history still contains images, so this comparison does NOT rule out retained media or payload size as a trigger, nor determine whether the underlying fault is in the client, server, or an intermediary. It should not be interpreted as proof that images are irrelevant, or that image count alone is the root cause.
The severe degradation appeared within the last two days; I previously worked with similarly large or larger image histories without this behavior. That onset and comparison are user observations, not a measured historical baseline. The impact spans desktop and VS Code workflows. Starting a fresh chat restores speed in this test, but leaves the existing working history and image references behind.
Please investigate why affected existing conversations enter repeated WebSocket failures and large HTTP replays, and provide a supported recovery that preserves their history and image references. If this transport symptom should be tracked separately from #33493, please advise. Updates, restarts and prior compactions have already been tried, as described in my earlier comment.
Only sanitized timings, byte counts, settings and the generic error string are shared here. No private conversation content, images, project names, local paths, process IDs or session identifiers are included.
dreampiggy commentedon Oct 9, 2026
The same issue. I have to use another Codex session to remove and filter all the image in the broken session, then resume the session and all issue fixed.
clean_codex_session_images.py
neohazuki commentedon Oct 13, 2026
Problema persiste. 88 dias sem resolucao