Repository navigation
Codex remote compaction deadlocks when input_image payloads remain in compacted replacement history #24388
Description
What version of Codex CLI is running?
codex-cli 0.133.0
What subscription do you have?
Pro Lite
Which model were you using?
gpt-5.5 with reasoning_effort=xhigh
What platform is your computer?
Linux 6.12.85+deb13-amd64 x86_64 unknown
What terminal emulator and version are you using (if applicable)?
GNOME Terminal, TERM=xterm-256color, COLORTERM=truecolor. No tmux/screen/zellij for the checked command environment. The affected sessions were long-lived local Codex app-server sessions launched through a wrapper, not ordinary one-off terminal chats.
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1779667950s since unix epoch",
"overallStatus": "fail",
"codexVersion": "0.133.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "/home/mrenda/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/home/mrenda/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "/home/mrenda/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "/home/mrenda/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "/home/mrenda/.codex/app-server-daemon/app-server-updater.pid (missing)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth env vars present": "OPENAI_API_KEY",
"auth file": "/home/mrenda/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/home/mrenda/.codex",
"config.toml": "/home/mrenda/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "/home/mrenda/projects/kuzys",
"model": "gpt-5.5",
"model provider": "openai",
"sqlite home": "/home/mrenda/.codex"
},
"remediation": null,
"durationMs": 0
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/usr/local/bin/codex",
"managed by npm": "true",
"managed package root": "/usr/local/lib/node_modules/@openai/codex",
"npm update target": "/usr/local/lib/node_modules/@openai/codex"
},
"remediation": null,
"durationMs": 392
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running npm on linux-x86_64",
"details": {
"platform": "linux-x86_64",
"version": "0.133.0"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/home/mrenda/.codex (dir)",
"active rollout files": "191 files, 5226839601 total bytes, 27365652 average bytes",
"state DB": "/home/mrenda/.codex/state_5.sqlite (file)",
"state DB integrity": "ok"
},
"remediation": null,
"durationMs": 385
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"COLORTERM": "truecolor",
"terminal": "GNOME Terminal",
"terminal size": "80x24"
},
"remediation": null,
"durationMs": 2
}
}
}What issue are you seeing?
Long-lived Codex sessions can become unable to resume after remote pre-turn compaction fails with context_length_exceeded.
The affected sessions had input_image payloads preserved inside the latest rollout type=compacted record, under payload.replacement_history.
When Codex later attempted remote pre-turn compaction, those image payloads were included in the compaction request. The request exceeded the model context window, and the session could not
continue.
User-facing error:
Codex ran out of room in the model's context window. Start a new thread or clear earlier history before retrying.
Error running remote compact task:
{
"error": {
"message": "Your input exceeds the context window of this model. Please adjust your input and try again.",
"type": "invalid_request_error",
"param": "input",
"code": "context_length_exceeded"
}
}
Logs included:
codex_core::compact_remote: remote compaction failed
compact_error.code = context_length_exceeded
Failed to run pre-sampling compact
Example logged sizes:
failing_compaction_request_model_visible_bytes=19638335
failing_compaction_request_model_visible_bytes=13626592
failing_compaction_request_model_visible_bytes=9091729
failing_compaction_request_model_visible_bytes=2172694
This creates a deadlock: the session needs compaction to continue, but compaction cannot run because its own input is too large.
What steps can reproduce the bug?
Uploaded thread: 019e0a2b-ffe3-7332-9276-6f6832f8680d
A minimal structural reproduction is:
- Use a long-lived Codex session that includes image inputs/screenshots.
- Let Codex perform remote compaction.
- Inspect the latest rollout
type=compactedrecord in~/.codex/sessions/.... - Confirm that
payload.replacement_historycontains a user message withcontentparts includinginput_image. - Continue the session until Codex tries pre-turn remote compaction again.
- Observe remote compaction failing with
context_length_exceeded.
Problematic data shape:
{
"type": "compacted",
"payload": {
"replacement_history": [
{
"type": "message",
"role": "user",
"content": [
{ "type": "input_text", "text": "..." },
{ "type": "input_image", "image_url": "data:image/png;base64,..." },
{ "type": "input_text", "text": "..." }
]
}
]
}
}
Observed affected local threads contained:
- 2 image payloads / ~530 KB image JSON
- 4 image payloads / ~669 KB image JSON
- 9 image payloads / ~2.0 MB image JSON
- 21 image payloads / ~9.2 MB image JSON
After manually replacing only those input_image parts in the latest compacted replacement history with small text markers, the sessions were able to resume without creating new threads.
Uploaded thread 019e0a2b-ffe3-7332-9276-6f6832f8680d contains the investigation and mitigation discussion. The actual affected private rollout files cannot be uploaded because they contain private
session data.
### What is the expected behavior?
Remote compaction should not preserve raw historical `input_image` payloads indefinitely inside active compacted replacement history.
Expected behavior would be one of:
1. omit `input_image` parts from compacted replacement history;
2. replace historical image payloads with lightweight markers;
3. summarize image existence without keeping base64/data URL payloads;
4. strip image payloads before sending history to the remote compaction model;
5. provide a built-in recovery path if compaction fails due to oversized historical payloads.
A session should not become permanently unable to resume because the compaction request itself exceeds context.
### Additional information
Local mitigation used successfully:
1. Back up `~/.codex`.
2. Create a single-file backup of the affected rollout.
3. Modify only the latest `type=compacted` record.
4. Inside `payload.replacement_history`, replace only `input_image` parts with small textual markers.
5. Leave thread id, DB metadata, message order, summaries, text content, and session pointers unchanged.
6. Validate JSONL after modification.
After this surgical repair, affected sessions resumed successfully without starting new threads.
This strongly suggests that preserved `input_image` payloads inside compacted replacement history are the direct trigger.
Privacy note: I cannot provide full rollout files because they contain private session data. I can provide redacted JSON shapes, exact log lines, structural counts, before/after hashes, and
details of the successful mitigation.
Activity
github-actions commentedon May 25, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
jshaofa-ui commentedon May 25, 2026
Fix: Remote Compaction Deadlock When input_image Payloads Remain in Compacted Replacements
Issue: openai/codex #24388
Component:
codex-rs/core/src/compact_remote_v2.rsSeverity: High — causes infinite compaction retry loops on conversations containing image inputs
1. Root Cause Analysis
1.1 Symptom
When a conversation thread contains user messages with
input_imagecontent items (base64-encoded image data URLs), remote compaction (v2) enters a deadlock cycle:InputImagepayloads1.2 Technical Root Cause
The deadlock originates in
codex-rs/core/src/compact_remote_v2.rsin the retained-message truncation logic:message_text_token_countreturns0forContentItem::InputImageitems. This is intentional for text token budgeting (images are billed differently by the API), but it creates a critical problem in the truncation loop:And critically,
truncate_message_text_to_token_budgetalways preservesInputImageitems:1.3 Deadlock Sequence
1.4 Why V1 Doesn't Have This Problem
The V1 remote compaction (
compact_remote.rs) uses the/responses/compactendpoint directly, which returns an encrypted compaction summary. The retained messages are filtered more aggressively:The V1 path relies on the server-side compact endpoint to produce a clean summary, and the client-side filtering is applied to the server's output, not the original input. The V2 path (
compact_remote_v2.rs) uses a streaming approach that retains original user messages fromprompt_input, which includes the raw image data.2. Step-by-Step Fix Approach
2.1 Overview
The fix strips
InputImagecontent items from retained messages during remote compaction v2, replacing them with a text placeholder. This ensures:2.2 Changes Required
File:
codex-rs/core/src/compact_remote_v2.rsThree functions need modification:
message_text_token_count— No change needed (correctly returns 0 for images; the fix is elsewhere)truncate_message_text_to_token_budget— ReplaceInputImagewith placeholder instead of preservingtruncate_retained_messages_for_remote_compaction— Add a pre-pass to strip images from all retained messagesAdditionally, add a new helper function to strip images from a single message.
2.3 Detailed Fix
Step 1: Add image-stripping helper
Add a new function to replace
InputImagecontent items with a text placeholder:Step 2: Modify
truncate_message_text_to_token_budgetReplace the line that preserves
InputImageitems:Step 3: Apply image stripping in
build_v2_compacted_historyAdd image stripping to the retained message pipeline:
2.4 Alternative Approach (Pre-pass)
If modifying
build_v2_compacted_historyis undesirable, apply stripping as a pre-pass intruncate_retained_messages_for_remote_compaction:3. Code Snippets
3.1 Full Patch for
compact_remote_v2.rs3.2 New Tests
4. Testing Strategy
4.1 Unit Tests
strip_image_content_from_message_replaces_images_with_placeholderstrip_image_content_from_message_returns_none_for_image_onlystrip_image_content_from_message_preserves_non_message_itemsbuild_v2_compacted_history_strips_images_from_retained_messagesbuild_v2_compacted_history_no_deadlock_with_many_imagesretained_history_truncation_strips_images_before_budgeting4.2 Integration Tests
remote_compact_v2_with_image_inputremote_compact_v2_no_context_overflow_after_compactionremote_compact_v2_multiple_compactions_with_imagesremote_compact_v2_image_placeholder_preserves_semantics4.3 Regression Tests
The existing tests in
compact_remote_v2.rsneed updates:retained_history_truncation_preserves_images_and_truncates_later_text_parts— This test currently expects images to be preserved. It must be updated to expect the placeholder instead.retained_history_truncation_charges_image_only_messages— Update to verify image-only messages become placeholder-only messages.retained_history_truncation_drops_image_only_messages_after_budget_is_spent— This test's behavior changes: image-only messages will now become placeholder-only messages, which have a non-zero token cost.4.4 Test Execution
5. Impact Assessment
5.1 Breaking Changes
None for end users. The fix is internal to the compaction pipeline. The placeholder text
[Image omitted during compaction]is visible to the model but is designed to be semantically clear.5.2 Behavioral Changes
[Image omitted during compaction]5.3 Performance Impact
5.4 Risk Assessment
5.5 Migration Path
No migration needed. The fix is applied at compaction time:
5.6 Monitoring Recommendations
After deployment, monitor:
6. Files Modified
codex-rs/core/src/compact_remote_v2.rsstrip_image_content_from_message, modifybuild_v2_compacted_history, modifytruncate_message_text_to_token_budgetcodex-rs/core/src/compact_remote_v2.rs(tests)7. Appendix: Related Code Paths
7.1 V1 Remote Compaction (
compact_remote.rs)The V1 path uses the
/responses/compactendpoint and does not retain original user messages with image payloads in the same way. Theshould_keep_compacted_history_itemfilter operates on the server's output, not the client's input. No changes needed for V1.7.2 Local Compaction (
compact.rs)Local compaction uses
collect_user_messageswhich extracts text viaUserMessageItem::message(). This method already ignores image content (returns empty string for non-text items). No changes needed for local compaction.7.3 Context Manager Normalization (
context_manager/normalize.rs)The
strip_images_when_unsupportedfunction already handles stripping images when the model doesn't support them. This is orthogonal to the compaction fix — it applies at request time, not compaction time. No changes needed.7.4 Token Estimation (
context_manager/history.rs)The
estimate_response_item_model_visible_bytesfunction already applies a heuristic for image token costs (RESIZED_IMAGE_BYTES_ESTIMATE = 7373 bytes ≈ 1844 tokens). This is used for context window management but not for compaction retention decisions. No changes needed, but confirms that images have significant token cost.Alexiom-Support commentedon May 27, 2026
I can confirm this failure mode on Windows / Codex Desktop as well.
In my case, a long-running Codex Desktop thread failed remote compaction with the same class of error:
stream disconnected before completion: unknown variant auto, expected high or originalThe relevant local evidence was in the latest
type:"compacted"JSONL record. Itspayload.replacement_historystill contained historicalinput_imageentries with largedata:image...payloads.A narrow local mitigation worked:
contentitems withtype:"input_image"insidepayload.replacement_historyinput_textplaceholder insteadAfter this, the thread could compact again. This was not solved by treating it as a generic context-window overflow; the image payloads in replacement history were the decisive factor.
xiehust commentedon Jun 3, 2026
I’m seeing what appears to be the same failure mode: a Codex thread that involved reading image inputs eventually hit a hard model context limit during continuation/compaction.
Environment:
Observed error:
etraut-openai commentedon Jun 29, 2026
Thanks for reporting this problem. Until recently, Codex used a separate endpoint and server-side logic for compaction. We recently switched to a more robust approach where the compaction logic is moved locally using the same endpoint as normal turns. This eliminates "remote compaction" errors and increases the stability and reliability of compaction operations. If you see further problems with compaction, please use
/feedbackand open a new bug report.nico883991-ui commentedon Jul 16, 2026
Codex Desktop on Windows still stalls for about 3–4 seconds after switching to a large existing thread. The thread becomes visible first, but the input box remains unresponsive during that period.
Environment:
Findings:
excludeTurns: true
initialTurnsPage: { limit: 16, sortDirection: "desc", itemsView: "full" }
reduced the initial response to about 52 KB and improved responsiveness.
Suggested improvements:
I can provide sanitized statistics, but cannot upload raw rollout files because they contain private conversation history.
ignatremizov commentedon Jul 19, 2026
A clarification for future readers: I believe “the compaction logic is moved locally using the same endpoint as normal turns” refers to remote compaction v2, introduced in #20773 and enabled by default for eligible providers in #27573.
This does not mean Codex returned to the older prompt-based local compaction path. In v2, Codex assembles the compaction request and rebuilds replacement history client-side, but sends a
compaction_triggerthrough the normal/responsestransport. The backend returns an opaquecompactionitem containingencrypted_content, which Codex carries into later turns. The dedicated/responses/compactrequest and server-rebuilt history are the parts that were replaced; the source still calls the new pathremote_compaction_v2.So “local” here describes orchestration and history reconstruction, not a plaintext local summarization turn. It also does not by itself establish that the original
input_imageretention issue was fixed: #23728 explicitly preserved non-text retained content such as images when rebuilding v2 history, and the current implementation still retains and counts those images.seb-veto commentedon Aug 3, 2026
I reproduced this on current
mainat8922a784fe6aa80683fe97c2dcdfdc361478aa7fand prepared a focused regression patch.Root cause: Remote Compaction V2 rebuilds replacement history from retained prompt items without removing
InputImagedata URLs. The replacement history is then used as input to later compactions, so the same base64 images are serialized again on every compaction. In one affected rollout, 84 original embedded images occupied about 76 MiB, while 57 compacted rows accumulated 3,349 embedded images and about 3.1 GiB.Proposed behavior:
[Image omitted after compaction]before installing compacted replacement history.Patch: seb-veto@47abb3e
Branch: https://github.com/seb-veto/codex/tree/fix/strip-images-from-compacted-history
Regression coverage includes unit tests for first/repeated compaction boundaries and an end-to-end Remote Compaction V2 test proving the first compaction request contains the image while the follow-up contains only the omission marker. Focused tests and all compaction/remote-compaction tests pass;
just fix -p codex-corepasses.The repository currently blocks external PR creation without a maintainer invitation. Please reopen this issue or invite a PR if this approach matches the intended fix; the branch is ready.
ignatremizov commentedon Aug 4, 2026
@seb-veto in my fork instead of omitting the image entirely after compaction, I give a link to the /tmp/codex-cliboard- path so the model can view_image if necessary after compaction - better performance if so. it then gets omitted in a subsequent secondary compaction