You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Codex App reopening some threads stalls because thread/resume returns oversized image-heavy payloads #26352
What version of the Codex App are you using (From "About Codex" dialog)?
26.601.21317
What subscription do you have?
Pro
What platform is your computer?
Microsoft Windows NT 10.0.19044.0 x64
What issue are you seeing?
Reopening some existing threads in the Codex App stalls noticeably. The backend appears to return very large thread/resume payloads when the rollout contains inline base64 image data.
What steps can reproduce the bug?
Reopen a thread whose rollout contains inline image data.
Observe a noticeable stall before the thread is usable.
Measured against the installed app-server:
thread/resume on large image-heavy rollouts returned about 7-20MB responses and took about 1.2-1.7s in backend time
the same calls with excludeTurns: true returned tiny responses and avoided the payload blow-up
Example local rollout sizes observed:
about 55.3MB
about 36.3MB
about 28.8MB
The large files contained data:image/png;base64,... inline content.
What is the expected behavior?
Reopening a thread should not require transporting and parsing multi-megabyte history payloads up front just to restore the thread shell.
The app-server protocol already has cheaper resume options such as excludeTurns, but the current reopen behavior appears to pay for full history transfer in image-heavy threads.
Why this is expensive:
Rollouts can contain inline data:image/png;base64,... content.
When that history is sent in the initial thread/resume response, the frontend has to transport, parse, and render very large JSON payloads before the thread is usable.
Likely fix directions:
Use metadata-only resume first for initial open.
Prefer excludeTurns: true or an equivalent metadata-first path.
Lazy-load turns/items after the shell is already open.
Add paging or truncation for image-heavy history so the initial resume response does not include unnecessary inline base64 content.
Notes from investigation:
thread/list itself was comparatively fast and small, so this does not look like a generic thread-listing problem.
The payload-size difference between normal resume and excludeTurns: true was much larger than the backend time difference, which suggests frontend JSON transport/parsing/rendering cost is likely a major part of the perceived freeze.
I could not open upstream PRs directly because GitHub returned GraphQL: 100yenadmin does not have the correct permissions to execute CreatePullRequest, but both branches are pushed to 100yenadmin/codex.
The lightweight branch makes the TUI request thread/resume with excludeTurns=true plus an initialTurnsPage, then replays that bounded page chronologically instead of consuming full thread.turns. It also exposes excludeTurns/initialTurnsPage in generated TS/JSON schemas so clients can use the documented cheap resume path.
The diagnostics branch is deliberately non-mutating. It logs resume payload shape for cold and running resumes: turn/item counts, estimated item payload bytes, item class distribution, image/user-image counts, tool-output counts, and token-usage replay duration. It does not log message text or raw session contents. That should make it easier to distinguish genuinely oversized image-heavy resumes from the smaller active-thread lockups now tracked separately in #28867.
Adding a small related macOS datapoint: my full details are in #22004 here: #22004 (comment)
This case was thread/read rather than thread/resume, but it looks like the same bounded-payload contract problem. A built-in image-generation-heavy rollout was 1,165,931,575 bytes, mostly duplicated inline image_generation_end / image_generation_callresult payloads. Opening/reading the thread produced a single logged JSON response line of 536,871,005 bytes and hit RangeError: Invalid string length in the Electron main process.
A local schema-safe repair that removed inline image bulk reduced the rollout to 6,096,679 bytes, with the same 3,240 JSONL records and max line 84,047 bytes. So the same excludeTurns / bounded tail-only loading direction discussed here likely needs to cover thread/read and stream-state snapshots too, not only thread/resume.
Activity
github-actions commentedon Jun 4, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
100yenadmin commentedon Jun 18, 2026
Related implementation branches published from current
main:I could not open upstream PRs directly because GitHub returned
GraphQL: 100yenadmin does not have the correct permissions to execute CreatePullRequest, but both branches are pushed to100yenadmin/codex.The lightweight branch makes the TUI request
thread/resumewithexcludeTurns=trueplus aninitialTurnsPage, then replays that bounded page chronologically instead of consuming fullthread.turns. It also exposesexcludeTurns/initialTurnsPagein generated TS/JSON schemas so clients can use the documented cheap resume path.The diagnostics branch is deliberately non-mutating. It logs resume payload shape for cold and running resumes: turn/item counts, estimated item payload bytes, item class distribution, image/user-image counts, tool-output counts, and token-usage replay duration. It does not log message text or raw session contents. That should make it easier to distinguish genuinely oversized image-heavy resumes from the smaller active-thread lockups now tracked separately in #28867.
100yenadmin commentedon Jun 18, 2026
@tibo-openai
thisislvca commentedon Jul 5, 2026
Adding a small related macOS datapoint: my full details are in #22004 here: #22004 (comment)
This case was
thread/readrather thanthread/resume, but it looks like the same bounded-payload contract problem. A built-in image-generation-heavy rollout was1,165,931,575bytes, mostly duplicated inlineimage_generation_end/image_generation_callresultpayloads. Opening/reading the thread produced a single logged JSON response line of536,871,005bytes and hitRangeError: Invalid string lengthin the Electron main process.A local schema-safe repair that removed inline image bulk reduced the rollout to
6,096,679bytes, with the same3,240JSONL records and max line84,047bytes. So the sameexcludeTurns/ bounded tail-only loading direction discussed here likely needs to coverthread/readand stream-state snapshots too, not onlythread/resume.