Repository navigation
Codex Desktop can crash or freeze when opening image-heavy sessions because session JSONL embeds base64 image payloads #28531
Description
What version of the Codex App are you using (From “About Codex” dialog)?
26.609.71450
What subscription do you have?
Business, pro
What platform is your computer?
Windows 11
What issue are you seeing?
Summary
Codex Desktop can crash in the Electron main process, or stall the desktop while
loading, when opening a session whose JSONL transcript contains many embedded
image/base64 payloads. The immediate failure observed was
RangeError: Invalid string length inside a main-process stdout/IPC write path,
but the root cause appears to be unbounded session JSON/stdout/IPC payload
construction for records that include large data:image/...base64 strings and
image generation results.
Environment
- App: Codex Desktop
- Platform: Windows 11
- Package version observed in stack trace:
OpenAI.Codex_26.609.9530.0_x64__2p2nqsd0c76g0
What steps can reproduce the bug?
Crash
A JavaScript error occurred in the main process
Uncaught Exception:
RangeError: Invalid string length
at JP.write (...OpenAI.Codex_26.609.9530.0_x64__2p2nqsd0c76g0\app\res...:21)
at Socket.onStdoutData (...)
at Socket.emit (node:events:508:28)
at addChunk (node:internal/streams/readable:563:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:514:3)
at Readable.push (node:internal/streams/readable:394:5)
at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)
Local Evidence
One affected local transcript had the following properties before repair:
- JSONL size: about
2.49 GB - Line count: about
12k - Largest single JSONL record: about
6 MB - Image/base64 related records:
- thousands of
data:image/...base64occurrences in tool output records - hundreds of
image_generation_endevents with multi-megabyte base64 result payloads
- thousands of
An image-only local repair externalized embedded image data and reduced that
transcript to about 25 MB while preserving parseable JSONL, prompts, text
outputs, and normal context.
Representative oversized record shape:
{
"type": "response_item",
"payload": {
"type": "function_call_output",
"output": [
{
"type": "input_image",
"image_url": "data:image/png;base64,..."
}
]
}
}Image generation events also store large payloads under fields like:
{
"type": "response_item",
"payload": {
"type": "image_generation_end",
"result": "<multi-megabyte base64 image>"
}
}What is the expected behavior?
Expected Behavior
Opening a previous session should not require materializing all historical
binary/image payloads as one giant JSON/stdout/IPC string. Image-heavy sessions
should remain navigable, with image artifacts loaded lazily or represented by
metadata until explicitly opened.
Actual Behavior
Opening the affected session caused Codex Desktop to crash in the Electron main
process with RangeError: Invalid string length. In related cases, app startup
and session list loading can also become visibly sluggish when the local Codex
state contains multi-GB JSONL/SQLite/session backup data.
In the same local state, after image payload repair, the remaining startup
stutter was correlated with text-heavy session JSONL files around 100-250 MB
each and a multi-GB local log SQLite database. Those files did not contain image
base64 and therefore could not be fixed by image externalization alone. This
points to a broader reader/indexing problem: startup and session list rendering
should not synchronously scan or materialize large historical transcripts or log
stores.
Moving those active session JSONL files out of .codex/sessions is not an
acceptable user-facing workaround for people who rely on the Codex Desktop GUI
as their preserved conversation archive, because it hides conversations from the
sidebar. The fix needs to preserve GUI discoverability while making loading lazy
and bounded.
Proposed Fix
This should be fixed at the session JSON I/O boundary, not only in the renderer UI.
1. Writer-side payload externalization
Do not write data:image/...base64 strings or raw binary-like blobs into session
JSONL. Store image/blob bytes in an artifact store such as a content-addressed
blob directory, and keep JSONL bounded:
{
"type": "artifact_ref",
"artifact_id": "sha256-or-uuid",
"path": "session-artifacts/<thread>/<artifact>.png",
"mime": "image/png",
"bytes": 2751234,
"width": 2048,
"height": 1152
}If existing schemas require a string field such as image_url, store a bounded
artifact URI string and maintain a sidecar mapper:
{
"image_url": "codex-artifact://session-payloads/<thread>/<sha256>.png"
}2. Reader-side lazy loading and pagination
- Load session JSONL incrementally.
- Do not materialize the entire transcript and all image payloads in one string.
- Elide any single event above a conservative byte threshold.
- Paginate large tool outputs and old image events.
- Teach image generation result readers to accept artifact refs, not only raw base64.
- Build the session list from a compact index rather than reading large JSONL
transcripts during startup. - Rotate or compact large local log SQLite stores so startup does not depend on
opening a multi-GB database.
Compatibility note: replacing image_generation_end.payload.result with an
artifact URI in current Codex Desktop builds can cause Invalid image generation result base64, because that field is still interpreted as raw base64. Until
renderer support exists, the reader should render a bounded placeholder instead
of sending an artifact URI to a base64 decoder.
3. IPC/stdout hard limits
Add a hard byte cap before main-process stdout/IPC response construction. If a
response exceeds the cap, return a structured truncation record instead of
attempting to allocate a huge string.
4. Migration/repair
On session open or background maintenance:
- detect legacy transcripts containing large
data:image/strings or raw image base64, - move blobs to artifact storage,
- replace inline image fields with bounded refs or explicit placeholders,
- preserve the original transcript in a backup location.
Why UI-only truncation is insufficient
The crash happens before a usable UI recovery path: the main process is already
trying to serialize/write oversized data. Renderer-side "show less" behavior
does not protect against unbounded main-process JSON/stdout/IPC writes.
Additional information
Suggested Regression Test
Create a fixture session JSONL containing hundreds of image records with 2-6 MB
base64 payloads. Opening the session should:
- not crash the Electron main process,
- keep peak memory bounded,
- show metadata/placeholders for old image payloads,
- allow explicit lazy loading of individual artifacts,
- preserve parseable session history.
Here are my local solution FYI, but I just would like to get the codex runtime fix solution.
https://github.com/Howard0401/codex-session-json-io-guard/tree/main
Activity
github-actions commentedon Jun 16, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Necmttn commentedon Jun 21, 2026
The session record contract should make blobs addressable. Store image bytes once in a blob/content store or sidecar keyed by hash, and write transcript records with mime, byte length, hash,
model_visible, and source ref.The UI can lazy-load previews by ref instead of sending base64 over stdout/IPC. Migration can keep old JSONL readable by converting repeated
data:imagepayloads into refs during index build.Generated with ax.
duofu5566-hash commentedon Jul 15, 2026
Additional reproducible case on Codex Desktop for Windows:
26.707.3748.0imageGenerationresult with inline base64.A metadata-only/lightweight thread listing remained safe. The crash was specifically triggered by serializing historical thread items containing the raw image payload.
Suggested regression coverage: cross-thread history/read APIs should elide or replace
imageGeneration.resultand other inline binary/base64 fields with bounded metadata, even when tool outputs are nominally excluded. The main-process stdout/IPC boundary should also enforce a hard byte cap and return structured truncation rather than constructing an unbounded string.cocoichisan commentedon Jul 28, 2026
Additional Windows case with a 5.02 GB image-heavy session
I have a closely matching Windows Desktop case with a larger rollout file and a Chromium crash signature.
Sanitized local audit findings:
5,016,108,657bytes (about 5.02 GB).data:image/ Base64 markers.codex.exeprocess at approximately 17.2 GB, with total system memory at 97%.0x80000003insidechrome.dll; source-level cause is not determined from the dump alone.The local thread ID, file path, username, and dump path are intentionally redacted.
This appears consistent with the issue's core diagnosis: historical inline image/Base64 payloads produce multi-GB session records, and opening/restoring the session causes unbounded hydration/serialization/rendering pressure in the Desktop/Chromium path. The app should reject, paginate, externalize, or lazily load such payloads rather than crashing or requiring recovery.
hewzhew commentedon Jul 29, 2026
Additional reproducible Windows case on current Desktop build
I reproduced the same task-specific crash on a much smaller, fully parseable local rollout.
Environment
26.721.4979.0x640.146.0-alpha.3.110.0.26200(build26200.8875), x64Observed behavior
source=vscodetask had a JSONL rollout of41,482,526bytes and3,939lines.state_5.sqlitereturnedPRAGMA quick_check = ok.Payload characteristics
The high-confidence trigger was oversized inline image data in tool outputs, not user-message image attachments:
10oversizedfunction_call_output/custom_tool_call_outputrecords13embeddeddata:image/png;base64,...images31,931,327bytes (~30.45 MiB)view_imageresults each contained one1848 × 2594PNG5,452,547bytesRepresentative record shape matches the report above:
{ "type": "response_item", "payload": { "type": "function_call_output", "output": [ { "type": "input_image", "image_url": "data:image/png;base64,..." } ] } }Local isolation / repair evidence
After preserving an untouched SHA-256-verified backup, I:
Result:
41,482,526bytes9,558,709bytes3,939/3,93900thread/readpath successfully returned the repaired task's recent turns and final answer.A read-only scan of 83 main local tasks found 16 additional tasks containing tool-output records over 512 KiB with inline image data. This suggests the failure can recur across otherwise normal image/PDF review workflows.
Expected behavior
view_imageresults should be externalized or referenced as artifacts instead of persisted as multi-megabyte Base64 strings.thread/read/ IPC should impose hard per-record and per-response byte caps.This also appears related to #35799, which reports background prefetch of an image-heavy rollout crashing the app without explicit navigation.
Privacy note: usernames, local paths, task titles, thread IDs, prompts, project content, images, and raw transcripts are intentionally omitted.
hewzhew commentedon Jul 29, 2026
Follow-up: I isolated a second, independent crash trigger after removing oversized inline image payloads from the affected rollout.
Environment: Codex Desktop on Windows, app package
26.721.4979.0(Browser Use release logged as26.721.41059).Observed sequence:
rightPanelOpenwasfalse.thread/readandthread/resumesuccessfully.Recoverable Chromium child process gone ... processType=GPU reason=crashed exitCode=101457950processType=GPU reason=launch-failed exitCode=18The desktop then exited.
thread-browser-tabs-v1:<thread>persisted atom. The rollout, database row, project assignment, and all other global state were left intact.browserTabId=null, no browser webview creation/attachment, no restored URL, and no new GPU child-process failure. The removed browser state was not recreated.This was separate from the large-inline-image problem: reducing the rollout from about 41.5 MB to 9.6 MB (while keeping all messages/reasoning and extracting images losslessly) was not sufficient by itself; the crash continued until the stale Browser Use tab state was removed.
Additional checks after the repair:
PRAGMA quick_check:okthread/read: succeedsExpected behavior: a hidden/stale Browser Use tab should not be restored as active when reopening a thread, and a Browser Use GPU child-process failure should not terminate the entire desktop app.
Polvoo21 commentedon Sep 2, 2026
Additional Windows case: 8.41 GiB rollout, 64 s
thread/read, then renderer crashThis is a content-safe report from another reproducible Windows installation. It strongly matches the unbounded inline-image/history hydration problem described in this issue.
Environment
10.0.22621, x6463.9 GiBOpenAI.Codex_26.825.6671.0_x640.151.0-alpha.7.226.831.2377.0withcodex-cli 0.152.1Sanitized affected-rollout measurements
9,027,292,196bytes (8.41 GiB)48,847180.5 KiB"type":"custom_tool_call_output":8,335data:image:2,424image_url:1,775"type":"image":3644-9 GiBrange.No task title, thread ID, prompt, image, local project path, or raw transcript is included here.
Exact failure sequence from Codex Desktop logs
All timestamps below are UTC on 2026-09-02:
The image/tool-heavy thread was opened/restored.
thread/resumetook33,289 ms.A
thread/turns/listrequest took37,462 ms.thread/readcompleted after63,955 ms; a concurrentthread/goal/gettook48,095 ms.Four seconds later, at
10:41:23Z, Codex Crashpad wrote a sidecar with:{"capture_kind":"crash","osarch":"x86_64","ptype":"renderer"}The main-process log then reported that the renderer/browser-use route window was no longer live, followed by
write EPIPEat10:41:48Z.Codex Desktop relaunched at approximately
10:41:51Z.The Windows Application log had no corresponding Application Error, Hang, WER, or resource-exhaustion event. Shortly after the incident, Windows still had approximately
37-40 GiBof free physical RAM. This makes system-wide OOM unlikely, although a renderer/V8/IPC allocation limit remains plausible.Crashpad also shows this is recurrent rather than a one-off: from 2026-08-31 through this incident it recorded nine Codex crashes on this installation, eight with
ptype=rendererand one withptype=browser.User-visible behavior
thread/readlatency.Expected behavior / why a new chat is not an acceptable workaround
Long-running project chats are intended to remain usable and will continue accumulating tool results and images. Users should not have to abandon working history whenever the rollout grows.
Codex should:
thread/reador renderer IPC;The raw session and full logs contain private project content and are therefore not suitable for a public attachment. The sanitized timing and aggregate measurements above are directly derived from the local Codex logs and rollout file.