Local compaction v2 retains unbounded input_image payloads, causing repeated auto-compaction

Open 💬 2 comments Opened Jul 16, 2026 by snrui

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:

  1. The post-compaction prompt remains close to the context threshold, causing immediate re-compaction on subsequent turns.
  2. 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

  1. Use a long-running Codex Desktop thread containing multiple pasted screenshots or image inputs.
  2. Continue until local/remote-v2 compaction occurs.
  3. Inspect the newest type: "compacted" rollout record.
  4. Confirm that payload.replacement_history retains user messages containing input_image data URLs.
  5. Send a short follow-up question.
  6. Observe another automatic compaction even though the previous compaction just completed.
  7. 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.rs
  • message_text_token_count
  • truncate_message_text_to_token_budget
  • truncate_retained_messages_for_remote_compaction

Possible fix direction

For active compacted history:

  1. Give retained images a conservative estimated token cost, using the existing image-size/detail estimator where possible.
  2. Add explicit caps for retained image count and/or encoded bytes.
  3. Keep the newest images first and replace older images with a lightweight placeholder once the image budget is exhausted.
  4. 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

  • #24388: same underlying retained-input_image problem, closed after the compaction architecture changed; this report reproduces the behavior on the newer local compaction path.
  • #24550: large inline images in compacted replacement_history cause Responses WebSocket fallback.
  • #24676: image-heavy rollout JSONL causes Codex Desktop hangs and difficult session recovery.
  • #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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗