Skip to content

Responses WebSocket falls back when compacted replacement_history contains large inline images #24550

Open

Description

@KwakYounHo

What version of Codex CLI is running?

codex-cli 0.133.0.

The original affected session metadata includes codex-cli 0.130.0, but the WebSocket fallback was reproduced from a copied session using the current Codex CLI/App around 0.133.0.

What subscription do you have?

ChatGPT Pro 20x.

Which model were you using?

gpt-5.5 with reasoning_effort=xhigh.

What platform is your computer?

macOS arm64.

What terminal emulator and version are you using (if applicable)?

iTerm2 3.6.10.

The affected thread was resumed through Codex CLI/App flows. The reproduction used copied CODEX_HOME directories and codex resume against the same session JSONL payload. No terminal multiplexer was intentionally used for the reproduction session; separate monitoring/caffeinate processes may have been run in other terminals.

Codex doctor report

Redacted summary from codex doctor --json:

  • overallStatus: ok
  • codexVersion: 0.133.0
  • install method: brew
  • runtime platform: macos-aarch64
  • installation: installation looks consistent
  • config: config loaded
  • model: gpt-5.5
  • model provider: openai
  • auth: auth is configured
  • auth mode: chatgpt
  • provider reachability: active provider endpoints are reachable over HTTP
  • WebSocket reachability: Responses WebSocket handshake succeeded
  • WebSocket handshake result: HTTP 101 Switching Protocols
  • WebSocket wire API: responses
  • terminal: iTerm2
  • terminal version: 3.6.10
  • terminal size: 211x98
  • state paths: state paths and databases are inspectable
  • active rollout files: 110 files, 1371653678 total bytes, 12469578 average bytes

I am not pasting the raw doctor JSON because it contains local usernames, paths, project names, and state locations.

What issue are you seeing?

A long-running Codex thread consistently falls back from Responses WebSocket to HTTPS when resumed.

This may share a root cause with #24388, because both involve large input_image payloads retained inside compacted replacement_history. The observed failure mode here is different: instead of remote compaction failing with context_length_exceeded, the Responses WebSocket stream closes before response.completed, retries several times, and then falls back to HTTP.

I was able to reproduce the fallback using a copied CODEX_HOME, and then eliminate the fallback by removing only inline image payloads from compacted replacement_history.

Observed WebSocket failure pattern:

stream disconnected - retrying sampling request (1/5 ...)
stream disconnected - retrying sampling request (2/5 ...)
stream disconnected - retrying sampling request (3/5 ...)
stream disconnected - retrying sampling request (4/5 ...)
stream disconnected - retrying sampling request (5/5 ...)
falling back to HTTP

The HTTP fallback then proceeds, but the affected thread becomes prone to long idle periods.

What steps can reproduce the bug?

I cannot attach the raw session JSONL because it contains local paths, prompts, tool output, and image payloads. The structural reproduction I used was:

  1. Start from a long-running Codex thread with multiple compactions and historical image inputs.
  2. Confirm that compacted replacement_history contains retained input_image payloads.
  3. Copy the affected session JSONL into a clean temporary CODEX_HOME.
  4. Resume the same thread from the copied session.
  5. Observe that WebSocket warmup succeeds, but the actual resumed turn payload fails over WebSocket, retries 5 times, and falls back to HTTP.
  6. Create another copy of the same session JSONL.
  7. Replace only input_image items inside type: "compacted" records with small text placeholders.
  8. Resume the same thread from that sanitized copy.
  9. Observe that responses_websocket completes normally, with no HTTP fallback and no stream disconnected retries.

Control reproduction with copied session JSONL:

  • WebSocket warmup succeeded.
  • The actual resumed turn payload failed over WebSocket.
  • stream disconnected occurred 5 times.
  • Codex fell back to HTTP.

This suggests the issue is reproducible from the session/history payload itself, not from the original live ~/.codex state.

Compaction-only sanitized reproduction:

  • I replaced only input_image items inside type: "compacted" records with text placeholders.
  • Non-compacted history was left mostly unchanged.

Before sanitization:

  • Session JSONL size: ~703 MB
  • Largest compacted JSONL line: ~17.4 MB
  • Total input_image entries: 1789
  • input_image entries inside compacted records: 1277
  • input_image entries outside compacted records: 512
  • Latest compacted replacement_history: ~17.39 MB
  • Inline image data in that replacement history: ~17.17 MB
  • Image data share of replacement history: ~98.75%

After compaction-only sanitization:

  • Session JSONL size: ~195 MB
  • Largest compacted JSONL line: ~241 KB
  • input_image entries inside compacted records: 0
  • input_image entries outside compacted records: 509

Result:

  • responses_websocket completed normally.
  • responses_http: 0
  • stream disconnected: 0
  • fallback: 0
  • turn completed successfully.

This suggests the failure is specifically tied to inline images retained in compacted replacement_history, not simply to any historical input_image item anywhere in the session JSONL.

I also tested a full-image-sanitized copy, where all input_image items were replaced with text placeholders. That also completed normally over WebSocket.

What is the expected behavior?

Either:

  • compacted replacement_history should not retain large raw inline image payloads unnecessarily,
  • the Responses WebSocket transport should handle this payload shape reliably,
  • Codex should surface a more specific close reason, such as payload size / unsupported multimodal replay / request shape issue, or
  • Codex should automatically recover from this history shape without requiring manual session JSONL surgery.

A session should not repeatedly fail Responses WebSocket and silently fall back to HTTP because compacted history retained large inline image payloads.

Additional information

The thread did not always fail WebSocket. It worked over WebSocket for several days before the first hard fallback.

The first hard fallback appeared after a compaction where replacement_history had grown to ~17.38 MB and contained 42 inline images.

In the latest compaction:

  • Messages in replacement history: 382
  • Messages containing images: 18
  • input_image count in replacement history: 42
  • Image bytes inside image-containing messages: ~99.88% of those messages

The images appear to be retained as historical user message content inside compacted replacement history.

Privacy note: I am not attaching raw rollout/session files because they contain private prompts, tool output, local paths, and inline image data. I can provide further redacted structural data if useful.

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    connectivityIssues involving networking or endpoint connectivity problems (disconnections)
    contextIssues related to context management (including compaction)
    on May 26, 2026
  2. github-actions commented on May 26, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  3. SylvainWinning commented on May 26, 2026

    @SylvainWinning

    This lines up with what I see in the repo as a plausible failure boundary:

    • compaction writes a CompactedItem with replacement_history: Some(new_history.clone())
    • rollout reconstruction later treats replacement_history as the live replacement history
    • WebSocket fallback is currently transport-level, so this specific payload-shape problem can look like a generic stream disconnect

    A useful narrow test case might be: construct a compacted rollout item whose replacement_history contains one or more input_image content items with large data URLs, resume/reconstruct it, and assert either that compacted history no longer carries raw image payloads into the next sampling request or that it is converted into a bounded placeholder before the WebSocket request is built.

    The key distinction in the report seems important: images outside compacted records did not trigger the fallback after sanitizing only compacted replacement_history, so I would keep the first fix/test scoped to compacted history rather than all historical multimodal content.

  4. VVittgenstein commented on Jun 1, 2026

    @VVittgenstein

    Adding a related Windows Desktop datapoint. Same underlying data shape as this issue (compacted.payload.replacement_history retaining inline images), but the observed symptom on my machine was Desktop session/rollout bloat and UI hangs rather than only Responses WebSocket fallback.

    Environment:

    • Codex Desktop Store package: OpenAI.Codex_26.527.3686.0_x64__2p2nqsd0c76g0
    • Platform: Windows x64
    • Date observed: 2026-06-01 local time

    Sanitized structural stats from the affected rollout:

    File size:                       102.44 MB
    JSONL lines:                     982
    Lines containing data:image:     31
    Bytes in image-containing lines: 100.37 MB
    data:image URI occurrences:      51
    Unique image data URIs:          34
    Total data:image URI bytes:      100.26 MB
    compacted records:               2
    bytes in compacted records:      24.74 MB
    largest single record:           24.207 MB compacted record with 16 image refs
    response_item user image lines:  14
    event_msg user_message images:   14
    

    Largest relevant record:

    Line 757: type=compacted, ~24.207 MB, 16 data:image refs
    

    Why I think this belongs here:

    • The rollout was not large because of ordinary text; almost all bytes were inline image payloads.
    • The same user-provided screenshots appear to be persisted in both response_item and event_msg forms.
    • Compaction then retained prior image-heavy history inside payload.replacement_history, creating another large durable record.
    • Quarantining the image-heavy rollout out of %USERPROFILE%\.codex\sessions restored Desktop responsiveness.

    I also added the broader Windows Desktop hang/session-list datapoint in #24676, but this comment is specifically for the compacted replacement_history amplification aspect.

    Privacy note: I am not uploading the raw rollout, screenshots, sqlite databases, or unsanitized logs because they contain private conversation/image data.

  5. FiredPhoenixIR commented on Jul 16, 2026

    @FiredPhoenixIR

    Windows confirmation: retained compacted images caused WebSocket fallback

    I reproduced this on:

    • codex-cli 0.144.4
    • Microsoft Windows NT 10.0.26200.0 x64
    • gpt-5.6-sol
    • reasoning_effort=max
    • OpenAI model provider

    The affected long-running session repeatedly produced:

    Reconnecting... 4/5
    Stream disconnected before completion: failed to send websocket request:
    IO error: An established connection was aborted by the software in your
    host machine. (os error 10053)
    

    It then fell back to HTTPS and remained on HTTPS after stopping and resuming the session. Other Codex sessions on the same machine continued working normally over WebSockets, suggesting this was specific to the affected session payload rather than general network or WebSocket availability.

    Structural measurements from the affected rollout before repair:

    • 7,241 JSONL records
    • 14 compacted records
    • 90.1 MiB rollout size
    • 52 input_image/data-URL occurrences
    • 13 original images plus 39 copies retained in compacted replacement histories
    • Latest compacted replacement_history: 82 items and 13 inline images
    • Approximately 24.9 million image data-URL characters in the latest active replacement history

    I closed the session, created a byte-identical backup, and performed a schema-aware cleanup:

    • Replaced only typed input_image objects with small input_text placeholders
    • Left encrypted_content and all other fields untouched
    • Preserved the session ID, message ordering, summaries, and all 7,241 JSONL records
    • Validated every JSONL record afterward

    After cleanup:

    • 0 input_image objects
    • 0 embedded image data URLs
    • Rollout size reduced from 90.1 MiB to 18.8 MiB
    • The same session resumed normally
    • Responses completed over WebSockets with no retries or HTTPS fallback

    This provides a Windows confirmation that the retained compacted image payload was causal. It appears to be the same retention mechanism described in #33493 and #23257.

    Uploaded affected thread: 019f6455-ea0e-7481-ba54-5c665a6ea33f

    Uploaded investigation and recovery thread: 019f6964-aa8c-7901-9dbb-90dd8249fb05

    Note: Both threads were uploaded through Codex feedback.
    The affected thread contains the original WebSocket failure and HTTPS fallback. The investigation thread contains the structural rollout analysis, schema-aware image cleanup, validation results, and confirmation that the same affected session resumed successfully over WebSockets afterward.

  6. i69699 commented on Aug 3, 2026

    @i69699

    I traced the generic disconnect message to the established Responses WebSocket close-frame path on current main (79479cd).

    The pump correctly forwards Message::Close(frame), but run_websocket_response_stream currently matches Message::Close(_) and discards both the close code and reason before returning:

    websocket closed by server before response.completed

    That means a server close such as 1009 / Message Too Big is indistinguishable from an ordinary transport disconnect in the CLI. This explains why an oversized image-heavy replacement_history can produce five generic reconnect attempts without surfacing the actionable server reason.

    I prepared a minimal diagnostic-only patch that:

    • preserves and reports the close code and reason, for example code=1009 reason=Message Too Big
    • keeps the existing generic message when the peer supplies no close frame
    • does not change retry or HTTP fallback behavior

    Patch commit: i69699@b1a2aaf

    Validation on Windows:

    • cargo +stable nextest run --no-fail-fast -p codex-api: 159/159 passed
    • cargo +stable clippy --fix --tests --allow-dirty -p codex-api: passed with no warnings

    The branch is ready if a maintainer would like to invite a PR.

  7. lanchuanshuang commented on Aug 13, 2026

    @lanchuanshuang

    macOS reproduction on the current Desktop build

    I added a detailed macOS corroboration to #36232. The retained inline-image/history shape also matches the root cause tracked here.

    Environment

    • Codex Desktop / ChatGPT app 26.803.61601
    • Bundled Codex CLI 0.147.0-alpha.6.5
    • macOS 26.5, Apple Silicon (arm64)
    • ChatGPT authentication with the built-in openai provider
    • gpt-5.6-sol, xhigh effort

    Two independent affected tasks

    • Task A: 109,259,104-byte rollout; latest active post-compaction history 48,758,514 bytes; 6 retained generated images contributed 21,413,208 bytes of inline base64.
    • Task B: 139,210,895-byte rollout; latest active post-compaction history 30,237,919 bytes; 7 retained generated images contributed 9,866,588 bytes of inline base64, plus several browser/screenshot records of approximately 2.4–4.7 MB each.

    In both tasks, a new turn records the user message but no subsequent model event before:

    stream disconnected before completion: error sending request for url (https://chatgpt.com/backend-api/codex/responses)

    Other tasks in the same Desktop process, on the same network and built-in provider, remained healthy. Task A's last successful model input was only 78,018 / 258,400 tokens, so serialized history byte size appears to be a pressure signal independent of the displayed token usage.

    The visible failure in these two cases reaches the HTTPS Responses endpoint rather than only surfacing a WebSocket fallback, but the causal payload signature is consistent: compaction leaves large inline image/tool payloads in the active history, and retrying the affected task resends them.

  8. i69699 commented on Aug 14, 2026

    @i69699

    I ran a separate local-only structural scanner against the pre-sanitization copy rather than installing another recovery tool. It does not access the network, SQLite databases, or the Codex app; it never emits source paths, session IDs, prompts, tool arguments, image contents, or source hashes. The source file's size and modification time were unchanged before and after scanning.

    Public-safe result:

    • Source rollout size: 496,800,226 bytes
    • JSONL records: 31,119; malformed records: 0
    • Compaction records: 98; records with replacement_history: 49
    • Largest compacted record: 16,776,971 bytes
    • Largest compacted record's replacement_history: 405 messages, 39 messages with images, 44 input_image/data-URL entries, 16,591,360 Base64 payload bytes (estimated decoded image bytes: 12,443,474)
    • Latest compacted record that still contains replacement_history: 16,763,158 bytes, 410 messages, 39 messages with images, the same 44 image entries and 16,591,360 Base64 payload bytes
    • Across retained replacement histories: 1,284 inline-image entries and 389,049,480 Base64 payload bytes
    • No record exceeded the scanner's 64 MiB bounded JSON parsing limit

    This independently confirms that the active compaction payload is dominated by retained inline image bytes. The attachments are the local read-only scanner and its public-safe JSON result.

    codex-issue-24550-public-evidence.zip

  9. i69699 commented on Aug 15, 2026

    @i69699

    I implemented a candidate fix for the retained inline-image payload issue:

    The change externalizes inline Base64 images returned in compacted replacement history into a content-addressed, per-thread store. Compacted history retains only bounded codex-image:// references, and view_image resolves a referenced image on demand with size, path, and SHA-256 validation. Remote image URLs remain unchanged.

    Targeted compaction, storage, integrity, path-safety, follow-up request, and view_image tests pass, as does just fix -p codex-core.

    I attempted to open a PR, but the compare page currently says that pull requests are limited to repository collaborators, and the API returns Resource not accessible by integration. Could a maintainer review/cherry-pick this commit, open a PR from the branch, or advise on obtaining collaborator access? I am happy to adjust the implementation based on review feedback.

  10. i69699 commented on Aug 21, 2026

    @i69699

    Thanks — this control makes the boundary between the two issues much clearer.

    We already have separate candidate transport fixes alongside the compaction/image change:

    • b11bc1b preserves the WebSocket close code/reason and maps an explicit 1009 / Message Too Big close to a non-retryable error, so that case does not retransmit the same request five times.
    • 8705dc2 prevents reuse of a WebSocket whose pump task has already stopped.
    • 844b237 addresses the payload source by externalizing compacted inline images into bounded content-addressed references with on-demand loading.

    The important remaining gap is that the first fix only applies when the peer provides a standard 1009 close frame. Windows socket errors 10053/10054 do not necessarily expose that close frame, so they can still enter the generic retry budget. PR #38681 also preserves fallback only for delegated sessions; your data suggests that later turns, Desktop reconstruction, or another Responses client recreation path may still lose the fallback state.

    I therefore think these should remain two independently reviewable fixes:

    1. bound/externalize retained inline images during compaction; and
    2. make oversized-request fallback durable across the task and reconstructed/derived clients, while avoiding five full retransmissions after the first deterministic oversized WebSocket failure.

    Your historical control is especially useful because it shows that payload size is the trigger, while the repeated retry/fallback scope is the latency regression.

  11. HyperHe-dev commented on Aug 29, 2026

    @HyperHe-dev

    Downstream amplification after HTTP fallback

    I think #24550 is part of a larger performance failure loop. The original report demonstrates much of the first half; I was able to reproduce and quantify the slowdown after HTTP fallback:

    view_image
    → inline image Data URLs retained in model-visible history
    → history grows without byte-based image truncation
    → loss of incremental WS state requires a full-history request
    → large WS request can fail and exhaust retries
    → HTTP fallback
    → subsequent model/tool loops repeatedly submit the accumulated history
    → additional view_image calls make each request larger
    → rapidly increasing post-tool latency and upstream traffic
    

    This does not prove that payload size alone caused the initial WS disconnect. It describes the amplification that occurs once the incremental path is lost.

    In rust-v0.150.0-alpha.12.2, view_image converts the file to a Data URL and returns it as an InputImage. The generic function-output truncation logic budgets text but copies InputImage payloads without accounting for their serialized bytes. Therefore, network history size can grow with every image even when token/context accounting does not reflect that growth proportionally.

    The WS path can send only incremental items when the request extends the previous one and previous_response_id remains available. Otherwise it sends the full active input. The HTTP fallback path rebuilds and submits the full prompt; I could not find an equivalent incremental path there.

    This means fallback can turn one large upload into approximately:

    S1 + S2 + S3 + ... + Sn
    

    where Sk is the accumulated active-history size at each model/tool loop.

    Compaction reset and reproduced the slowdown

    One affected task remained in HTTP fallback throughout the following experiment:

    • Immediately after successful compaction: 17 active items, 0 images, followed by post-tool intervals of 6s, 5s, and 9s.
    • After view_image use resumed: 25 inline images / approximately 84.8 MiB of Data URLs, followed by intervals of 5m28s, 5m25s, and 6m09s.

    Across 53 measured intervals, the median was 3m00s and the maximum was 9m18s, while the tool executions themselves had a median runtime below one second.

    The transport state did not change: reducing the accumulated history temporarily restored normal latency, and rebuilding the image history reproduced the slowdown.

    These images were added by ordinary view_image calls after compaction; they were not images retained inside the compaction's replacement_history.

    Macro impact

    A separate affected task ran for approximately 8h05m and contained:

    • 300 serial model-response cycles
    • 296 tool calls/results
    • 3 compactions
    • approximately 6.95 GB of Codex upstream traffic

    Approximately 26,983 seconds / 7.50 hours, or 92.8% of the entire task, were spent between tool completion and the next model-produced response item.

    This is end-to-end latency rather than pure upload time, but it shows where the dominant cost accumulated.

    The two behaviors that seem worth addressing independently are:

    1. unbounded retention of raw inline image payloads in model-visible history, including ordinary view_image outputs;
    2. repeated full-history submission after the incremental path is lost.

    Fixing either would reduce the severity; fixing both would break the failure loop.

  12. i69699 commented on Aug 31, 2026

    @i69699

    Thanks for the downstream-amplification analysis. I hardened the candidate implementation against the additional failure modes discussed here and pushed the updated single commit:

    What this candidate now covers:

    • externalizes Base64 images from compacted message and function/custom-tool output history into a content-addressed local store;
    • persists bounded codex-image:// references and reloads exact bytes through view_image only on demand;
    • avoids reloading the same historical image repeatedly while it is already present in the active model context;
    • sanitizes existing affected rollouts in memory during resume/fork, while leaving the source JSONL unchanged;
    • uses atomic writes, hash verification, path validation, deduplication, and 50 MiB/image, 256 MiB/thread, 1 GiB/global limits;
    • fails closed to a bounded unavailable placeholder if storage/validation fails, rather than retaining the oversized Base64 payload.

    This intentionally does not claim to solve the other half of the loop: new ordinary view_image outputs can still accumulate after a compaction until the next compaction, and WebSocket retry / durable HTTP-fallback state is unchanged. Those remain independent transport/live-history fixes.

    Validation completed:

    • 10 compacted-image unit tests
    • 30 rollout-reconstruction tests
    • 15 compact-remote core tests
    • 3 V2 end-to-end image-budget cases
    • 1 V1 end-to-end follow-up case
    • cargo clippy -p codex-core --lib --tests -- -D warnings

    I attempted to open the PR directly, but GitHub returned i69699 does not have the correct permissions to execute CreatePullRequest. If the direction is acceptable, could a maintainer invite collaboration, open a PR from the comparison, or cherry-pick the commit?

  13. dreampiggy commented on Oct 9, 2026

    @dreampiggy

    Additional macOS CLI reproduction on 2026-10-09, with recovery after removing historical images from the same resumed thread. This closely matches this issue and #33493; HTTP-only transport did not by itself restore responsiveness in this observation.

    Environment

    • codex-cli 0.156.1, macOS 26.6.2, Apple Silicon, ChatGPT authentication.
    • Model: gpt-6-astra, reasoning effort medium.
    • Long-lived thread originally created with CLI 0.153.4; five persisted compaction records.
    • The before/after HTTP tests explicitly used --no-daemon. This has not been retested on the latest CLI release.

    Observed sequence

    1. Resuming the existing thread through the built-in OpenAI provider repeatedly established the Responses WebSocket connection successfully, then failed sending the request:

      stream disconnected before completion: failed to send websocket request: IO error: Broken pipe (os error 32)
      

      The CLI retried through Reconnecting ... /5. Successful handshakes distinguish this observation from a simple failure to establish a WebSocket connection.

    2. Explicitly resumed with an HTTP-only provider:

      [model_providers.openai-http]
      name = "OpenAI HTTP"
      base_url = "https://chatgpt.com/backend-api/codex"
      wire_api = "responses"
      requires_openai_auth = true
      supports_websockets = false
      stream_idle_timeout_ms = 60000
      stream_max_retries = 5
      codex resume <thread-id> --no-daemon -c 'model_provider="openai-http"'

      Logs confirmed transport="responses_http". At 12:03:18 UTC+8 the sampling request started; when checked around 12:06:48 there was still no assistant response or recorded request error. The user then exited the CLI. This was an interrupted wait, not a measured terminal HTTP timeout.

    3. With the thread stopped, backed up the rollout and replaced historical input_image content with small text markers, including images inside compacted.payload.replacement_history and guardian_history. Local image references and embedded screenshot data in UI/tool metadata were also removed. Text, tool records, thread ID, ordering and timestamps were preserved.

    4. Resumed the same thread with the same HTTP-only configuration and no intentional network/hosts changes. The sampling request started at 12:08:28; an assistant message was recorded at 12:08:35, and the turn finished at 12:08:36. The user confirmed the session worked again.

    Structural measurements

    Measurement Before cleanup
    Rollout file 89,084,478 bytes / 8,012 JSONL records
    input_image occurrences in original response_item records 21
    input_image occurrences across the complete persisted rollout, including repeated compaction snapshots 90
    Images in the latest compacted replacement_history 20
    Additional response-item image after that compaction 1
    Last completed request's reported input tokens 183,000 of a 258,400-token context window

    After cleanup: 31,284,707 bytes, a reduction of 57,799,771 bytes. Codex's thread/read successfully parsed all 134 turns. The first successful post-cleanup turn reported 178,578 input tokens.

    These are persisted-rollout sizes, not measured serialized request or wire sizes. The 90 occurrences include historical snapshot duplication; I am not claiming all 90 images or the whole 89 MB file were sent in one request. The active replacement history retained 20 images, with one subsequent image.

    Interpretation and recovery request

    The before/after result strongly implicates retained image history as a contributor to the unusable thread. It does not establish a precise byte limit, a WebSocket 1009 close code, which network component failed, or how much of the HTTP wait was upload versus backend processing. This was a natural before/after comparison across process restarts, not a controlled repeated benchmark; WebSocket behavior after cleanup was not retested.

    IPv6 was considered because of #27381, but the affected process's observed TCP connections were IPv4. DNS routed the endpoint through private IPv4 addresses in this environment, so an intermediary remains a possible contributing factor. No hosts pinning or IPv6 setting change was needed for the observed recovery.

    A supported remove historical images while preserving text/tool history recovery command would be valuable, alongside bounded retained-image bytes and actionable transport diagnostics. Manual JSONL editing is not a clean public workaround: although inference recovered, the local history projection subsequently warned thread-store internal error: durable rollout shrank before projection. An official operation should keep the durable rollout and derived history index consistent.

    Only aggregate diagnostics and sanitized error text are included here; no private conversation, screenshots, session identifiers, account credentials or project paths are attached.

  14. anvsk commented on Oct 14, 2026

    @anvsk

    Windows reproduction: retained screenshot payloads, repeated 10053 disconnects, and successful recovery after removing images from the latest compacted snapshot

    Adding new diagnostic evidence to #24550 rather than opening a duplicate. Related: #33493 and #40323.

    Environment

    • Incident: 2026-10-14, approximately 17:17–17:48 Asia/Shanghai (UTC+08:00). Log excerpts below use UTC.
    • Codex Desktop client version reported in app-server logs: 26.1007.71326.
    • Bundled executable verified with --version: codex-cli 0.162.0-alpha.17.2.
    • Windows x64, Microsoft Windows NT 10.0.26200.0.
    • ChatGPT Pro authentication. Failures occurred with both gpt-6-astra and gpt-6.1-sol; the successful recovery test used gpt-6.1-sol.
    • A local HTTP/HTTPS proxy was configured. This is an important confounder; the evidence does not identify which network component aborted the connection.

    User-visible failure and reproduction sequence

    One long-running local conversation became unresponsive while other conversations remained usable. The affected conversation had accumulated screenshots used for temporary visual checks; most were no longer needed.

    1. Continue a long-running screenshot-heavy thread after context compaction.
    2. Send a short follow-up such as 继续 (continue).
    3. The turn starts but produces no assistant reply. Stopping and continuing does not resolve it.
    4. Logs show repeated Responses WebSocket disconnect/retry cycles. Successful WebSocket connection messages appear before subsequent disconnects.
    5. Archive/unarchive the same conversation and send a diagnostic instruction to only reply with a short confirmation, without tools or project work. This also stalls and logs another 10053.
    6. Attempt built-in thread/compact/start. The request is accepted, but remote compaction also fails with 10053 and retries.

    This is an observed existing-thread reproduction, not a clean synthetic minimal reproduction. The private original transcript and screenshots are not attached publicly.

    Measured before/after comparison

    The latest compacted snapshot was created at 2026-10-14T07:52:39.367Z. Its payload.replacement_history contained 29 complete inline input_image data URLs. One additional image was present in subsequent history.

    Measurement Before recovery After recovery
    Inline images in that compacted replacement history 29 0
    Serialized replacement-history size 15,999,289 bytes 244,242 bytes
    Normal diagnostic turn No reply; disconnect/retry Completed in 14,040 ms
    Final diagnostic reply None 恢复测试成功 (“Recovery test successful”)
    Conversation status after the final test — Idle; completed turn

    The snapshot size decreased by approximately 98.47%. Sizes were measured consistently using Python len(json.dumps(replacement_history).encode()), with default serialization. They are local history serialization measurements, not captured HTTP/WebSocket payload sizes. We did not capture wire traffic and cannot claim that exactly 16 MB was uploaded on every request.

    The complete rollout before the image repair was 607,846,787 bytes. This is persistent transcript size, not the request size.

    The last recorded successful model usage before the incident was 169,660 input tokens with a reported 258,400-token context window. The final recovery test reported 149,698 input tokens, 149,376 cached input tokens, and 7 output tokens. These are different turns, not a controlled token-cost experiment; cached input tokens must not be interpreted as billed quota loss.

    Redacted log evidence

    Trace prefixes, turn IDs, local paths, project content, and credentials are omitted. The error message means “An established connection was aborted by the software in your host machine.”

    2026-10-14T09:33:36Z codex_core::responses_retry
    stream disconnected - retrying sampling request (2/5 in 385ms)...
    retries=2 max_retries=5
    sampling_error=stream disconnected before completion: IO error:
    你的主机中的软件中止了一个已建立的连接。 (os error 10053)
    
    2026-10-14T09:35:00Z codex_core::responses_retry
    stream disconnected - retrying sampling request (3/5 in 862ms)...
    retries=3 max_retries=5
    sampling_error=stream disconnected before completion: IO error:
    你的主机中的软件中止了一个已建立的连接。 (os error 10053)
    
    2026-10-14T09:41:58Z codex_core::responses_retry
    stream disconnected - retrying sampling request (1/5 in 189ms)...
    retries=1 max_retries=5
    sampling_error=stream disconnected before completion: IO error:
    你的主机中的软件中止了一个已建立的连接。 (os error 10053)
    
    2026-10-14T09:44:09.469044Z WARN codex_core::responses_retry
    remote compaction v2 stream failed; retrying request after delay
    retries=1 max_retries=2
    compact_error=stream disconnected before completion: IO error:
    你的主机中的软件中止了一个已建立的连接。 (os error 10053)
    
    2026-10-14T09:45:33.891878Z WARN codex_core::responses_retry
    remote compaction v2 stream failed; retrying request after delay
    retries=2 max_retries=2
    compact_error=stream disconnected before completion: IO error:
    你的主机中的软件中止了一个已建立的连接。 (os error 10053)
    

    Recovery evidence from the same conversation's persisted events:

    2026-10-14T09:47:39.687Z task_started
    2026-10-14T09:47:52.695Z assistant: 恢复测试成功
    2026-10-14T09:47:53.727Z task_complete
    started_at=1791971259 completed_at=1791971273 duration_ms=14040
    

    Exact local recovery and limitations

    After backing up and hash-verifying the rollout, the failed isolated compaction process was stopped and the conversation was archived to release its writer. Only the latest compacted record's 29 inline images were replaced with text references to preserved local image files. The rest of the transcript lines were verified byte-for-byte unchanged at the time of repair. Original transcript image entries, text, and the newer image were retained. The conversation was then restored and the diagnostic message retried.

    This was a manual workaround, not a supported product fix or a recommendation to edit live rollout files. Archive/unarchive without the image replacement had already failed. The final diagnostic succeeded after the replacement. This is strong evidence of a history-shape/retained-image sensitivity, but not proof of a particular transport size limit or which component caused 10053. Only a short recovery test was validated; long-term stability was not established.

    Requested product changes

    1. Bound retained image count and encoded byte size during compaction; prefer summaries or retrievable references for old temporary screenshots.
    2. Avoid repeatedly serializing historical image blobs into active compacted history; deduplicate persisted media where appropriate.
    3. Provide a supported per-conversation “drop old image context / optimize history” recovery option that preserves transcript and text continuity.
    4. Surface actionable diagnostics for repeated connection failures, including payload/history size indicators, instead of leaving a short follow-up apparently stuck.
    5. Review quota accounting for failed retry and compaction loops.

    Account impact and compensation request

    The affected user reports that this incident wasted one Codex usage-reset card and an additional nearly 50% of their usage allowance. They request two complimentary usage-reset cards (two usage resets) as compensation.

    This consumption impact is the user's report; the attached technical evidence does not independently attribute or quantify account-level quota charges. Please investigate the account usage and consider granting the two requested resets. If compensation must be handled by OpenAI Support rather than GitHub maintainers, please route or advise the appropriate private support path. Private transcripts and authentication credentials are intentionally excluded from this public report.

    Affected OpenAI / ChatGPT account email: anvskyi@gmail.com. This is the email recorded in the affected user's current local Codex ChatGPT login. The account owner explicitly requested including it here so OpenAI can identify the account for investigation and, if approved, credit the two requested usage resets to this account. Please perform any additional account verification privately through OpenAI Support.

  15. pamelagrainsn-cmyk commented on Oct 16, 2026

    @pamelagrainsn-cmyk

    Additional Windows reproduction: image-heavy saved thread fails WebSocket sends (10054), falls back to large HTTP requests; synthetic probe receives close 1009 above 16 MiB

    The affected user reviewed and explicitly authorized publication of this redacted diagnostic report.

    Summary

    An existing image-heavy Codex desktop thread repeatedly reconnects and falls back from WebSocket to HTTP. Subsequent productive requests repeatedly prepare approximately 63 MB compressed HTTP bodies containing historical image data.

    Restarting the desktop app did not resolve the problem. A separately authorized, “reply only OK, do not use tools” diagnostic turn in the affected thread took approximately 76 seconds and also fell back to HTTP.

    Separate synthetic tests, containing no conversation history or screenshots and using generate:false, observed a size boundary: a message of 16 MiB minus one byte completed, while 16 MiB plus one byte received a peer WebSocket Close 1009.

    Please investigate aggregate request-byte handling and provide a supported recovery path that preserves the original thread and all records.

    Environment

    • Windows desktop app, x64
    • Desktop version: 26.1013.31256, build 20586, production channel
    • App updater result: up_to_date
    • Windows package version: 26.1013.3656.0
    • Embedded Codex runtime: 0.163.0-alpha.10.2
    • Model: gpt-6.1-sol, reasoning effort high
    • Authentication: ChatGPT sign-in
    • Network: VPN/TUN connection through a Singapore exit
    • VPN core: sing-box 1.13.14-1e6be962c

    The embedded runtime version above is not a claim about OpenAI’s server-side version.

    1. Actual affected-thread behavior

    On October 17, 2026, approximately 01:38–01:39 UTC+08:00, one diagnostic turn requested only “OK,” with no tools or continuation of the original task.

    Observed sequence:

    1. WebSocket connections established successfully.
    2. Request attempts subsequently failed with Windows socket error 10054.
    3. Five retry warnings containing 10054 were recorded.
    4. The client logged “falling back to HTTP.”
    5. The HTTP request body metrics were:
    Metric Bytes
    Before compression 85,426,260
    After compression 63,086,542

    The turn eventually returned “OK” after 75,797 ms.

    The original task was not continued.

    2. Evidence of repeated historical-image payloads

    For the preceding ten consecutive model requests in the affected thread:

    • All ten used responses_http.
    • Prepared compressed bodies ranged from 62,989,383 to 63,084,833 bytes.
    • Their combined prepared compressed size was 630,481,387 bytes.
    • Nine had recorded HTTP 200 responses; the last was interrupted before completion.
    • No retry events were identified within those ten request intervals.

    A local structural reconstruction of active history found 44 historical inline image entries, totaling 83,663,628 data-URL bytes in each reconstructed request history. No new image entries were added during the inspected turn.

    These are historical image entries repeatedly carried forward—not 44 newly captured screenshots per request.

    Measurement limitation: image composition was reconstructed locally, not obtained from captured HTTP bodies. Prepared-body metrics are not exact wire traffic or VPN billing measurements; interrupted requests may not have transmitted their entire bodies.

    3. Separate low-traffic synthetic size control

    These tests did not use the affected thread, conversation text, screenshots, or model generation.

    Conditions:

    • Same Singapore VPN route
    • Official Codex Responses WebSocket endpoint
    • generate:false
    • Minimal valid JSON padded with whitespace
    • permessage-deflate negotiated
    • TLS certificate issuer: Google Trust Services
    • Reported edge location: SIN
    Uncompressed message size Result
    4,096 bytes response.completed
    16,777,215 bytes — 16 MiB minus 1 response.completed
    16,777,217 bytes — 16 MiB plus 1 Peer Close 1009
    4,096 bytes afterward response.completed

    The measured outbound WebSocket frames totaled approximately 34 KB, excluding TLS/TCP and handshake overhead.

    4. Interpretation and limits

    The evidence strongly suggests that aggregate inline-image bytes can exceed an operational WebSocket message-size boundary, preventing successful initialization and subsequent incremental continuation.

    However:

    • The actual thread recorded 10054, not a preserved peer Close 1009.
    • The synthetic 1009 result must not be presented as the captured close reason for the original thread.
    • No controlled alternate-network comparison was performed.
    • VPN involvement in every historical disconnect has not been excluded.
    • The observed 16 MiB boundary is not asserted to be a documented, permanent limit across all OpenAI endpoints.

    5. Existing image-reference work does not establish legacy recovery

    Relevant official changes include:

    The inspected history-replay implementation in the installed runtime, stable 0.163.0, and preview 0.164.0-alpha.1 continues to use InlineAttachmentStore without uploading or migrating recorded images.

    Therefore, support for file references does not by itself recover an existing inline-image-heavy thread.

    This evidence is being added to #24550 rather than opening a duplicate issue.

    Requested resolution

    Please:

    1. Identify whether a released desktop/runtime version fixes this specific legacy-history failure.
    2. Provide a supported migration that replaces model-facing inline images with durable attachment references while preserving:
      • The original thread ID
      • All displayed messages and screenshots
      • Tool-call relationships and history integrity
      • Image access after restart and resume
    3. Check aggregate serialized bytes before repeated WebSocket retries, rather than relying only on token budgets.
    4. Preserve actionable close/error diagnostics and clearly disclose HTTP fallback.
    5. Avoid repeatedly resubmitting unchanged oversized history without a bounded recovery decision.

    Privacy

    This report contains no conversation bodies, screenshots, Base64 image data, local paths, thread identifiers, account identifiers, authentication tokens, cookies, or authorization headers.

    Please request narrowly scoped, redacted diagnostics if additional evidence is necessary, rather than requiring a full conversation upload.

  16. pamelagrainsn-cmyk commented on Oct 16, 2026

    @pamelagrainsn-cmyk

    请求明确修复版本,或提供保留原线程和全部截图的官方恢复流程

    我们希望继续使用官方Codex客户端,不替换运行时、不安装请求改写代理,也不通过迁移线程或删除截图绕过问题。

    截至2026年10月17日,我们核实的环境为:

    • Windows桌面客户端:26.1013.31256,build 20586,生产渠道;更新检查返回 up_to_date。
    • 内置运行时:0.163.0-alpha.10.2。
    • ChatGPT登录,内置OpenAI提供方,模型 gpt-6.1-sol。

    已有证据

    1. 受影响的原线程出现Windows错误10054、多次WebSocket重试及HTTP回退。一次诊断请求压缩前约85.4 MB、压缩后约63.1 MB。
    2. 本地结构检查发现,活跃历史中44个内嵌图片条目合计约83.7 MB;连续请求反复携带这些旧图片,即使没有新增截图。
    3. 独立的合成数据对照中,TLS证书验证正常、客户端接收大小限制关闭:16 MiB少1字节的消息成功,16 MiB多1字节的消息收到远端首先发出的1009关闭帧。两者压缩后发送的数据帧均为16,803字节。测试未使用私人聊天、截图,也未请求模型生成。
    4. 我们没有把合成测试的1009直接等同于原线程每一次10054;原始重置的具体来源仍需要官方结合服务端记录确认。

    请官方明确回答

    一、是否已有修复版本?

    请提供具体桌面版本、内置运行时版本及对应修复链接,并说明修复是否:

    • 适用于已经积累大量图片的旧线程,而不只是新线程;
    • 避免对确定超限的请求原样反复重试;
    • 解决HTTP回退后重复上传大图片历史的问题。

    如果尚未发布,请说明问题是否已确认、由哪个问题单跟踪,以及是否有预计发布窗口。

    二、如果尚无修复版本,请提供官方恢复流程。

    我们要求恢复流程能够:

    • 保留同一个线程ID、完整聊天记录和全部原始截图;
    • 不手工修改JSONL、SQLite或分页偏移;
    • 不安装非官方运行时或第三方代理;
    • 恢复正常的WebSocket增量续接,避免每轮重复上传整批旧图。

    请明确:官方 /compact 是否适用于这种已经超限的线程;如果压缩本身也失败,应使用什么受支持的恢复机制。请不要仅建议反复重试、重启、换网络或新建线程。

    我们愿意提供必要的脱敏版本、时间、请求大小及错误信息,但本次不会上传聊天记录、截图、认证信息或完整日志。如需补充,请明确最小诊断字段及官方私密提交渠道。

    这是对正常长线程及截图工作流造成实际影响的可靠性问题。我们需要明确的官方修复或恢复路径,而不是自行改造客户端。

  17. pamelagrainsn-cmyk commented on Oct 16, 2026

    @pamelagrainsn-cmyk

    补充:官方 /compact 成功,但活跃上下文仍保留约71 MB内嵌旧图片

    在此前反馈的同一原线程中,我们通过桌面客户端原生 /compact 命令尝试恢复,没有修改配置、安装补丁或迁移线程。

    环境仍为:

    • Windows桌面客户端:26.1013.31256,build 20586
    • 内置运行时:0.163.0-alpha.10.2
    • ChatGPT登录、内置OpenAI提供方、gpt-6.1-sol

    本次结果

    2026年10月17日,UTC+8:

    • 03:11:18开始,03:13:43完成,耗时约145秒。
    • 压缩任务状态为 completed,没有任务错误,随后线程空闲。
    • 本次压缩请求使用 HTTP,返回状态200。
    • 请求体压缩前 85,428,801字节,zstd压缩后 63,088,186字节。

    注意:以上是执行压缩所发送的请求大小,不是压缩后下一轮请求的大小。

    压缩后仍存在的大图片上下文

    只读检查新生成的压缩检查点:

    项目 压缩前 压缩后
    活跃历史中的内嵌图片条目 44 30
    内嵌图片Data URL总字节数 83,663,628 71,136,517
    压缩后文件式图片引用数量 — 0

    压缩后保留的图片均位于用户消息中,仍为完整内嵌Data URL,并未转换成小型引用。图片字节仅减少约15%。

    这里的71.14 MB是内嵌Data URL的序列化字节数,不是解码后的图片文件大小,也不是压缩后实际网络上传量。完整替换历史的紧凑JSON序列化大小为 71,334,919字节。

    压缩后客户端记录的上下文用量约为 88,058 tokens。这说明,Token上下文明显下降,并不意味着序列化请求已足够小。

    尚未验证的部分

    我们没有发送任何后续测试消息,也没有继续原任务。因此:

    • 没有测得压缩后下一轮真实请求大小;
    • 不能宣称WebSocket增量连接已恢复;
    • 也不能宣称压缩后的请求已经再次收到1009。

    但目前保留的内嵌图片字节,仍明显超过此前合成对照中观察到的约16 MiB消息边界,连接重建时仍存在完整上下文过大的风险。

    请官方明确说明

    1. /compact 成功后仍保留约71 MB用户图片,是否属于预期行为?
    2. 如果属于预期行为,客户端如何在连接缓存丢失后,重新建立不超限的WebSocket基线?
    3. 是否已有按序列化字节数管理图片上下文、或将历史图片转换为可按需读取引用的官方修复?
    4. 请提供明确修复版本,或能够保留原线程ID、完整记录和全部截图的受支持恢复流程。

    本次仅提供脱敏统计;不上传私人聊天、图片、认证信息或完整日志。

  18. pamelagrainsn-cmyk commented on Oct 16, 2026

    @pamelagrainsn-cmyk

    Follow-up: successful /compact still followed by repeated ~53 MB HTTP request bodies

    A read-only inspection of existing local logs found that successful manual compaction did not eliminate large HTTP requests in the affected conversation.

    Environment

    • Windows desktop package: 26.1013.3656.0
    • Bundled runtime: 0.163.0-alpha.10.2

    Observed evidence

    • Manual /compact completed successfully on October 17, 2026, at 03:13:43 UTC+8.
    • The resulting replacement history retained 30 inline image items in user messages. Their Data URL strings totaled 71,136,517 bytes.
    • At inspection time, the post-compaction logs contained 10 HTTP request-body compression records:
      • Before compression: approximately 71.45–71.67 MB per request.
      • After zstd compression: approximately 53.22–53.28 MB per request.
      • Corresponding response logs recorded HTTP 200.
    • These ordinary model requests were explicitly marked responses_http.
    • Earlier logs recorded WebSocket failures followed by falling back to HTTP. We found no evidence establishing successful incremental WebSocket continuation after compaction.

    Measurement limitations

    These are logged serialized/compressed request-body sizes, not packet captures or VPN billing totals. The ten records represent approximately 532 MB of prepared compressed request bodies. We did not inspect request payload contents, so we cannot prove that every request contained exactly the same 30 images, although the retained-history and request-body sizes are consistent with that explanation. The initial connection-failure cause remains unconfirmed.

    No dedicated reproduction requests were initiated during the inspection; however, the diagnostic conversation itself involved model calls and should not be treated as a zero-traffic operation.

    Requested official resolution

    Please provide either:

    1. The specific desktop and bundled-runtime versions containing a verified fix for repeated large requests after compaction and transport fallback; or
    2. A supported in-place recovery procedure that preserves the original conversation and all screenshots, reduces stale inline-image bytes in the active working context, and restores incremental continuation where technically supported.

    Please clarify whether retained-image byte budgeting and recovery from sticky HTTP fallback are tracked separately, and identify the owning issue or fix PR.

    Starting a new conversation, deleting screenshots, manually editing session files, or installing a custom runtime does not satisfy the requested recovery requirements.

    Only sanitized versions, counts, byte sizes, timestamps, and generic transport markers are included here. No conversation records, screenshots, raw logs, request bodies, credentials, cookies, or authentication headers are attached.

  19. pamelagrainsn-cmyk commented on Oct 16, 2026

    @pamelagrainsn-cmyk

    Follow-up: multiple threads repeatedly submit large HTTP bodies during successful model/tool progression—not failure retries

    We performed a read-only correlation of the latest 10 logged HTTP request-body compression records from each of four existing local conversations.

    Environment

    • Windows desktop package: 26.1013.3656.0
    • Bundled runtime: 0.163.0-alpha.10.2
    • Observed on October 17, 2026, UTC+8.
    • No test requests, task resumptions, configuration changes, or history modifications were performed during this inspection.

    Results

    Anonymized conversation Compressed body per request Successfully completed model steps Failure retries identified Interrupted Unclassified
    A — previously reported conversation 53.22–53.28 MB 10 0 0 0
    B 14.68–15.73 MB 9 0 1 0
    C 8.62–8.63 MB 10 0 0 0
    D 2.27–2.34 MB 10 0 0 0
    Total: 40 request records — 39 0 1 0

    “Successful progression” means successful model/tool execution steps, not an assessment that every business action was useful.

    Why these are not simply repeated failed requests

    We did not classify requests as successful based on HTTP 200 alone. We correlated:

    • Request timestamps and turn identifiers.
    • Corresponding HTTP response status logs.
    • New model output items and completed tool calls.
    • Post-sampling state markers emitted on the successful sampling-result path.
    • Explicit retry and interruption records.

    For conversation B, the final request started at 00:47:18. The conversation recorded turn_aborted with reason interrupted at 00:47:36. This was an interrupted new step, not a failure retry.

    Earlier WebSocket failures exist in the broader investigation. The conclusion here is narrower: no failure retries were identified among these 40 sampled HTTP request-body records.

    Retained media remains relevant after compaction

    • Conversation A retained 30 user-image items totaling 71,136,517 Data URL bytes after successful manual compaction.
    • Conversation C retained 19 user-image items totaling approximately 11.52 MB in its latest compacted history. No new image items appeared in the subsequent persisted response-item suffix, yet its sampled HTTP requests remained approximately 8.6 MB compressed each.
    • Conversation B retained user images and subsequently accumulated additional tool images.
    • Conversation D had no images in its latest compacted checkpoint but accumulated new tool images afterward.

    These observations distinguish old-image retention after compaction from new tool-image accumulation.

    Impact and measurement limits

    The sampled records total approximately 796 MB of prepared zstd-compressed request bodies, despite predominantly successful progression.

    These are request-layer measurements—not packet captures, confirmed WAN delivery totals, VPN billing totals, or proof that every outgoing body contained exactly the same images.

    The practical concern is that ordinary successful model/tool loops can repeatedly incur large full-context submissions, even without an active failure-retry loop. Reducing retries alone would not resolve this behavior.

    Requested official resolution

    Please provide:

    1. Explicit desktop and bundled-runtime fix versions, identifying the relevant issue/PR and whether already-affected conversations are repaired.
    2. A supported in-place recovery procedure that preserves the original thread identity, complete inspectable history, and every screenshot while removing unnecessary repeated inline-media transmission from the working context.
    3. Clarification of how incremental continuation can be safely restored after HTTP fallback, and what prevents retained or newly accumulated media from recreating the problem.

    Migration, screenshot deletion, manual JSONL/database editing, or a custom runtime does not satisfy the requested recovery requirements.

    No private conversation content, screenshots, raw logs, request bodies, credentials, cookies, authentication headers, local paths, or thread identifiers are attached.

  20. pamelagrainsn-cmyk commented on Oct 17, 2026

    @pamelagrainsn-cmyk

    Follow-up: file-ID support exists, but historical inline images are not converted during replay — request for a supported, non-destructive recovery path

    Related: #45971. This follows up on the retained-image / WebSocket fallback evidence already reported in this issue.

    We have continued a read-only review of the official source to distinguish support for image file references from recovery of existing image-heavy threads.

    Official source findings

    1. PR #45794 added support for image file_id references in inputs and tool outputs.

    2. PR #46065 routed prepared images through AttachmentStore before recording history. Its description explicitly states that history replay remains on InlineAttachmentStore, does not upload recorded images, and preserves existing file references.

      Consequently, replaying an existing thread does not automatically upload its historical inline images to attachment storage and replace them with file IDs. “Not uploading during replay” here refers to attachment-store uploads; it does not establish that those inline bytes are omitted from subsequent full model requests.

    3. We compared the relevant public source paths in:

      • The installed runtime: 0.163.0-alpha.10.2
      • Stable runtime: 0.163.0
      • Prerelease runtime: 0.164.0-alpha.3

      As checked on October 18, 2026, these versions retain the historical replay path using InlineAttachmentStore. For example: replay code in 0.164.0-alpha.3.

    4. PR #54398 stores generated-image results as attachment references. Its scope does not establish that it converts existing user screenshots or eliminates historical input-image retransmission.

    These are runtime/source observations, not a claim that a particular desktop update has already shipped an end-to-end recovery feature.

    Additional local, read-only observations

    For one successful compaction at 2026-10-18 06:54:51 UTC+08:00:

    • The working context contained six inline image entries both before and after compaction.
    • Their Data URL strings totaled 18,044,944 bytes.
    • Per-image content hashes matched across the compaction boundary, confirming that the same image contents remained rather than being replaced with short file references. No hashes or image contents are included here.
    • Multiple WebSocket disconnections and HTTP fallback followed. Request-layer logs recorded HTTP request bodies of approximately 18.40 MB before compression and 13.69 MB after compression.

    The structural measurements and request-body sizes are not packet-capture measurements or VPN billing totals. They do not independently identify the disconnecting endpoint or prove a particular WebSocket size limit.

    Requested official clarification and recovery path

    1. Please provide a supported operation to convert historical inline images in an existing thread's working context into valid, durable attachment references while preserving:

      • The original thread identity, all messages, and all screenshots;
      • The ability to view historical screenshots;
      • Project associations and task continuity.
    2. Can such a conversion prevent the old image bodies from being resent in full after resume, compaction, or reconnect? Please clarify reference permissions, lifetime, and recovery if a reference expires or becomes unavailable.

    3. If a fix already exists, please identify:

      • The fixing PR or commit;
      • The first runtime version containing it;
      • The corresponding desktop client version;
      • Whether existing threads are repaired automatically or require an official conversion/recovery procedure.
    4. If this is not yet supported, please confirm the limitation and provide an official temporary recovery method. Creating a new thread, deleting screenshots, manually editing history, or installing a third-party runtime does not meet this recovery request.

    Privacy: This comment contains only redacted structural measurements and public source links. No conversation transcript, screenshot, raw log, local thread identifier, credential, cookie, or authentication header is attached or uploaded.

  21. pamelagrainsn-cmyk commented on Oct 17, 2026

    @pamelagrainsn-cmyk

    Follow-up: new Desktop screenshots also remain inline — please distinguish enabling file-ID storage for new images from recovering historical images

    This supplements the previous follow-up. Related: #45971.

    The affected user explicitly authorized this redacted follow-up. No conversation transcript, screenshot, raw log, credential, or local thread identifier is attached.

    Scope and environment

    Read-only checks on October 18, 2026:

    • Windows Desktop package: 26.1013.4894.0.
    • Installed native runtime: 0.163.0-alpha.10.2.
    • The running cached runtime and the runtime bundled in the installed package have matching SHA-256 hashes.
    • No configuration/history changes, screenshot uploads, model test requests, or task restarts were performed for this investigation.

    Evidence: the local app-server defaults to an inline image store

    In the official source corresponding to the installed runtime:

    1. The app-server passes codex_core::passthrough_image_store() when constructing the thread manager:
      message_processor.rs.

    2. passthrough_image_store() constructs InlineAttachmentStore:
      thread_manager.rs.

    3. Despite the method name upload(), this implementation simply returns Ok(UploadResult::Inline { bytes: request.data }); it does not perform a remote attachment upload or produce a file ID:
      attachment-store/src/lib.rs.

    4. Image preparation distinguishes three outcomes: successful inline bytes, a successful file reference, and an upload error that falls back to inline:
      image_preparation.rs.

    This distinction matters: an image can remain inline because that is the successful default storage result, not necessarily because a remote upload failed.

    Local observations agree with the default-inline path

    In the affected thread's existing persisted model-history records:

    • Two newly added user images at 06:14:40 UTC+08:00 were already inline Data URLs when first recorded.
    • One newly added user image at 06:50:43 UTC+08:00 was also inline when first recorded; its Data URL totaled 4,245,470 bytes.
    • These were not images that became inline only after a later compaction.
    • We found no matching failed to upload prepared image warning in the retained native logs or the current Desktop-session logs.

    The absence of a warning is not proof that every upload attempt succeeded: native per-thread logs have bounded retention, and the logs do not expose a per-image storage-result trace. The stronger evidence is the default source path together with the persisted input representation. We are not claiming to have instrumented the live storage object or captured an attachment upload.

    No documented end-user switch was found

    We checked the official configuration reference and the installed runtime's configuration schema. We did not find a supported setting that selects a remote image attachment store or enables automatic file-ID conversion for new screenshots.

    PR #45556 establishes the upload/resolve integration interface, but does not itself document a Desktop end-user activation procedure. Likewise, general Files API / image file_id support does not establish a supported bridge into an existing Desktop local thread using ChatGPT authentication.

    Please correct this finding if a documented switch or supported activation flow exists.

    Requested official answer: two separate deliverables

    A. Prevent new image accumulation

    Please specify whether the shipping Windows Desktop local-thread path can upload each new screenshot once and record a durable file_id instead of inline Base64.

    If supported, provide:

    • The exact supported UI steps or configuration key;
    • Minimum Desktop and native-runtime versions;
    • Any account, rollout, authentication, or backend prerequisites;
    • Whether it can apply to an existing thread without migrating it or changing its history/project associations;
    • File-reference access, lifetime, and failure-recovery behavior.

    If not supported, please confirm the limitation and identify the fixing PR, release/version, or supported temporary alternative. A developer integration hook alone is not an end-user recovery procedure.

    B. Recover existing inline-image history

    Separately, please provide a supported, non-destructive procedure to convert the working context's historical inline images while retaining the original thread identity, all messages and screenshots, screenshot viewability, project associations, and task continuity.

    Enabling file-ID storage only for future screenshots would not remove the historical inline-image burden already documented in this issue. Please do not treat these as the same fix.

    We want to remain on the official Desktop client without manual history edits, deleted screenshots, third-party runtimes, or request-rewriting proxies.

    Privacy: Only redacted structural measurements and public source links are submitted. No private conversation, images, raw logs, credentials, or authentication headers are uploaded.

  22. pamelagrainsn-cmyk commented on Oct 18, 2026

    @pamelagrainsn-cmyk

    Follow-up: Latest source still retains sticky HTTP fallback and inline-image history replay — request for a confirmed fix or supported recovery procedure

    Following our previous reports, we reviewed the latest public stable release, rust-v0.163.0, and the current main-branch snapshot:

    8c076942cb70fba8e5c4732c23f1faabc4aed484

    The desktop updater also reported up_to_date for our production channel. We understand this only confirms update eligibility, not that the reported issue has been fixed.

    Source findings

    1. HTTP fallback remains sticky across turns.

    The stable release and inspected main snapshot contain the same test:

    websocket_fallback_is_sticky_across_turns

    It explicitly verifies that, after WebSocket failures trigger HTTP fallback, the next turn remains on HTTP without another WebSocket attempt.

    Stable-release test
    Inspected main-branch test

    2. Replayed inline images are not automatically migrated to file references.

    The inspected history-replay implementation explicitly avoids uploading or migrating recorded history and uses InlineAttachmentStore. Existing file references are preserved, but legacy inline images remain inline.

    History-replay implementation

    3. Compaction does not impose a serialized-image byte budget.

    The inspected compaction implementation retains messages and images using a token budget. This does not guarantee that the resulting working context is small enough in serialized bytes to avoid large reconnect requests.

    Compaction implementation

    Why this matters

    We understand that a new connection using store=false may need to replay the required working context after the documented 60-minute connection limit. We are not asking to reuse an expired connection-local cache.

    The concern is the combination of:

    • Inline historical images remaining in the working context after compaction.
    • Reconnect attempts replaying that large context.
    • HTTP fallback remaining active across subsequent turns.

    Previously reported request-layer logs showed repeated large HTTP request bodies after fallback. These are not measurements of individual WebSocket frames, and we are not claiming that a particular WebSocket size limit has been conclusively established.

    Requested response

    Please provide either:

    1. A confirmed fix version, including the desktop release and bundled runtime version—not only a merged PR or CLI release; or
    2. An officially supported recovery procedure that preserves the original thread, complete visible history, and every screenshot, while restoring incremental operation without repeatedly transmitting legacy inline-image bytes.

    Please clarify separately:

    • How to recover from sticky HTTP fallback.
    • Whether existing inline images can be converted to durable references without deleting or rewriting visible history.
    • Whether any proposed procedure affects only the target thread or other active sessions.
    • Whether recent attachment-reference improvements cover existing user/tool screenshots, rather than only generated-image items.

    No chat archives, screenshots, complete request bodies, credentials, cookies, or authentication headers are attached to this follow-up.

  23. pamelagrainsn-cmyk commented on Oct 18, 2026

    @pamelagrainsn-cmyk

    Follow-up: token-bounded compaction still retains multi-megabyte user images; requesting a supported, history-preserving fix

    Please investigate and provide an actionable resolution for this issue. It has repeatedly disrupted the affected user's work and amplified traffic and waiting time. We need a confirmed fix version, or an official recovery procedure that preserves the original thread, all visible history, and all original-resolution images.

    Refined failure chain and evidence boundaries

    1. Connection lifetime and reconstruction. The Responses WebSocket connection lifetime is approximately 60 minutes. A replacement connection cannot simply reuse the old connection-local continuation cache. Connection expiry, compaction/history changes, and other lost incremental preconditions can require a full working-context request. This is not limited to automatic compaction.

    2. Large complete messages, not just large compact requests. Earlier bounded synthetic testing observed a 16 MiB message-size boundary and remote close code 1009. This is an observed boundary for the tested route, not a claimed officially documented universal limit. A full reconstruction can encounter the same risk whether caused by expiry, compaction, or another reset. Existing production logs include five-attempt retry sequences; compact and ordinary-generation retry budgets must not be conflated. The later generic “websocket closed by server before response.completed” failures do not themselves establish close code 1009 or prove a size-related cause.

    3. Successful compaction is not a serialized-byte safeguard. In the inspected successful compaction on October 18, twelve tool-image occurrences left the active working window while three user images remained. This is a result of this particular compaction, not a claim that every compaction removes every tool image. Retention uses model-oriented token estimates, which do not guarantee a bounded serialized request body.

    4. Repeated HTTP uploads are an implementation/path behavior, not an HTTP requirement. On the inspected Codex client/subscription path, ordinary HTTP model requests do not use server-side context continuation; each request submits the required working context again. Retained inline image bytes are therefore resubmitted, including in normal tool-result-to-model continuations, not only failed retries. A local tool execution alone is not being counted as a model upload.

    In short: retained images enlarge the working context → loss of incremental preconditions requires full reconstruction → large complete messages increase the risk of WS failure → HTTP fallback repeatedly carries the required working context and inline images. This explains the measured large-payload samples; it does not establish that all disconnects or slow responses have this cause.

    Latest privacy-safe measurements

    The latest successful compaction committed at 2026-10-18 13:51:49 UTC+8:

    • Three retained user images, all 1439 × 810, PNG, detail=high.
    • Image binary sizes: 2,575,714; 1,547,862; 1,509,144 bytes.
    • Combined binary size: 5,632,720 bytes.
    • Combined inline data-URL size: 7,510,362 bytes.
    • Each retained image's data URL is byte-identical to its counterpart in the preceding successful compaction.
    • More than 99% of each PNG is encoded image data (IDAT), not ancillary metadata.
    • Subsequent existing HTTP request logs recorded:
      • 13:53:26: 7,782,653 bytes before zstd / 5,765,396 bytes after zstd.
      • 13:53:49: 7,797,939 bytes before zstd / 5,772,789 bytes after zstd.
      • Both subsequently logged HTTP 200.

    These are application-recorded request-body sizes, not packet-capture measurements or VPN billing totals. We did not generate new model requests to collect this follow-up. Earlier evidence in this issue separately documents approximately 53 MB compressed HTTP requests; those older samples should not be conflated with this smaller post-compaction window.

    Relevant upstream work checked

    As of this check, the latest public stable release is rust-v0.163.0, and main is 8c07694.

    • #40280 accounts for retained images in the token budget; it is not an aggregate serialized-byte limit or image re-encoding policy.
    • #46065 routes new prepared images through the attachment store, but explicitly keeps recorded-history replay on InlineAttachmentStore rather than uploading or migrating old inline images.
    • Current history reconstruction separates prepared working history from the unchanged recorded rollout, but no supported desktop interface was found for replacing only these retained images with smaller working copies.
    • A closely matching independent report describes retained PNG payloads despite compaction_image_budget being enabled. Its JPEG measurements are corroborating user evidence, not an official mitigation or a promised compression ratio for our images.

    Requested resolution

    Please provide:

    1. A maintainer-confirmed fix/PR and exact release version, including desktop availability, addressing aggregate serialized image bytes and repeated full-context HTTP submission/recovery.
    2. An official same-thread recovery procedure that preserves all messages and original images, while converting only the model-facing copies of the three retained images into smaller derivatives or supported reusable attachment/file references.
    3. If such a procedure is not available, confirmation of that gap and an engineering owner/tracking plan. Repeated migration, deleting screenshots/history, manually editing compacted output, replacing official binaries, or installing an unofficial proxy is not an acceptable supported recovery procedure.
    4. Clarification of the applicable WS complete-message limit and how the client is expected to recover when a canonical compacted window or reconnect bootstrap exceeds it, without repeatedly sending the same rejected payload.
    5. A way to validate recovery using privacy-safe byte/count/transport telemetry, without uploading private transcripts or repeatedly replaying a large task.

    This comment contains only sanitized structural measurements and public source links. No chat transcript, screenshots, image payloads, credentials, cookies, or authentication headers are attached.

  24. pamelagrainsn-cmyk commented on Oct 18, 2026

    @pamelagrainsn-cmyk

    Additional provenance evidence: TLS verification enabled, Google Trust Services issuer recorded, and peer-initiated 1009 at the observed 16 MiB boundary

    Following the previous clarification, a read-only review recovered the original October 17 test output, which contains stronger TLS and close-frame provenance than the separate October 18 Rust diagnostic. Please use the following to clarify the enforcing layer and provide a supported recovery path.

    No network test was rerun for this review. No private transcript, image, raw log, credential, or authentication header is attached.

    Original test: October 17, 2026, UTC+08:00

    Target: wss://chatgpt.com/backend-api/codex/responses.

    The preserved test code and structured output establish:

    • The Python test created an ssl.create_default_context() and passed that context to the WSS connection.
    • Runtime output recorded tls_check_hostname=true and tls_verify_mode=2 (ssl.CERT_REQUIRED).
    • The certificate issuer returned by the TLS connection was organizationName=Google Trust Services; commonName=WE1; countryName=US.
    • The WebSocket upgrade returned HTTP 101 and negotiated permessage-deflate.
    • The client receive limit was explicitly max_size=None.
    • The test passed proxy=None to the Python WebSocket client. This disables that client's explicit proxy selection; it does not prove the traffic bypassed VPN/TUN routing.
    • No explicit custom-CA loading or verification-disabling code was found in that test invocation.
    Start time, UTC+08:00 Complete logical request size Observed result
    02:27:18 16,777,215 bytes (16 MiB minus 1) response.completed
    02:27:21 16,777,217 bytes (16 MiB plus 1) Peer-initiated Close 1009, then client Close 1009
    02:27:25 4,096 bytes response.completed

    The over-limit exception recorded rcvd_then_sent=true, received code 1009, and sent code 1009. Thus this was a received peer Close followed by the client's reply, not merely a locally generated receive-size exception. The tiny request after the rejection also completed.

    The two boundary payloads used the same synthetic JSON content, differing only by two trailing JSON whitespace bytes. No private screenshots or conversation were used. Exactly 16,777,216 bytes was not tested.

    What this establishes, and what remains missing

    The evidence strongly points away from ordinary router/VPN TCP forwarding performance as the direct explanation of this particular size-dependent, valid WSS Close 1009. A non-terminating TCP forwarder does not normally have access to the decompressed WS message or the ability to originate an authenticated application Close frame inside the destination TLS session.

    However, we are not claiming complete forensic exclusion of every intermediary:

    • The historical full certificate chain, leaf DER/fingerprint, subject/SAN snapshot, and trust-store/environment snapshot were not preserved.
    • No explicit custom CA was loaded by the test code, but inherited trust configuration or a locally trusted interception root cannot be conclusively excluded from that fact alone.
    • No custom-CA/HTTPS-interception evidence was found in the examined AirTCP application logs; absence from those logs is not proof that interception was impossible.
    • No alternate-network comparison is available.
    • The certificate issuer text alone is not certificate pinning and does not identify which OpenAI/CDN/backend component enforced the limit.
    • These findings concern the synthetic 1009 sample; they do not assign all historical 10054 resets or generic stream closures to the same cause.

    Maintainer clarification and resolution requested

    Please provide a concrete answer to these questions:

    1. Where is the observed approximately 16 MiB limit enforced on this Codex subscription WSS route? Is it the TLS-terminating edge/CDN, an application gateway, the Responses WebSocket service, or another component? If OpenAI does not expect this boundary, please identify the additional sanitized evidence needed to investigate.
    2. Is the relevant limit on an individual frame, the reassembled complete message, compressed bytes, or decompressed bytes? Is the limit documented, configurable, or specific to this route? We have not found an official public 16 MiB Responses WS specification.
    3. How should the official client safely recover when a reconnect bootstrap or canonical compacted working window exceeds that byte limit, rather than retrying the same oversized request and remaining in repeated full-context HTTP uploads?
    4. Please provide an exact fixed release/desktop build, or an official same-thread recovery procedure preserving every message and every original-resolution image, while converting only model-facing copies of retained user images into smaller derivatives or supported reusable references.
    5. If no supported recovery exists today, please confirm that gap, identify the owning team/tracker, and provide the planned fix. Deleting history/screenshots, repeated migration, manual JSONL/SQLite edits, or replacing the official binary is not an acceptable supported recovery procedure.

    Related implementation and lifecycle evidence is already recorded above in this issue. This supplement adds only sanitized TLS settings, issuer information, timing, sizes, and close-order observations; it does not upload the underlying conversation, screenshots, raw logs, or credentials.

  25. AmuroRX-93 commented on Oct 18, 2026

    @AmuroRX-93

    Please add an optional “Load historical attachments on demand” setting.
    In long-running chats, Codex repeatedly includes historical inline images in HTTP requests. On my Mac, compressed request bodies were around 10–17 MB, even on turns without new images.
    With this setting enabled, please preserve the original attachments and transcript, keep summaries and references in the active context, and reload old attachments only when needed.
    Please make this work with existing chats. Repeatedly starting new chats disrupts ongoing tasks.
    Environment: macOS desktop app, bundled Codex CLI 0.162.0-alpha.17.2.

  26. niel-malan-sa commented on Oct 18, 2026

    @niel-malan-sa

    Please provide a supported desktop fix that stops repeated uploads of historical images while preserving existing chats, their visible history, and the visual context needed to continue working.

    Additional redacted observations from macOS:

    • Desktop version: 26.1013.40934, build 20770.
    • Bundled runtime: 0.163.0-alpha.10.2.
    • The official desktop updater reports up_to_date for this installation.
    • Image-heavy chats repeatedly prepare compressed HTTP request bodies up to approximately 33 MB. Retained inline images account for most of the payload. These are logged request-body sizes, not a claim about carrier-billed traffic.
    • Request compression is already enabled.
    • A small fresh-process request completed over WebSocket on the same network. This does not establish that the affected image-heavy histories would succeed over WebSocket.

    Could maintainers identify a fixed desktop build, a supported setting, or an official recovery procedure that:

    1. Prevents unchanged historical image bytes from being resent with successive requests, including after HTTP fallback.
    2. Preserves existing chats, original images, and usable visual context.
    3. Can be applied without interrupting active work, repeatedly compacting chats, starting replacement chats, or manually editing history files.

    If stored image references are the intended solution, please clarify how desktop users can enable them and recover existing inline-image histories. Please distinguish a permanent fix from a temporary connection reset.

    The affected work is being kept running. No private images, transcripts, raw logs, credentials, or local chat identifiers are attached.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workingconnectivityIssues involving networking or endpoint connectivity problems (disconnections)contextIssues related to context management (including compaction)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions