# Codex desktop main-process crash: `RangeError: Invalid string length` when loading sessions whose rollout JSONL exceeds V8's max string length

Open 💬 6 comments Opened May 10, 2026 by inGramGames
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

  • OS: Windows 11 (x64) - Install: Microsoft Store — C:\Program Files\WindowsApps\OpenAI.Codex_<version>_x64__2p2nqsd0c76g0\app\ - Versions reproduced on: 26.506.2212.0 and 26.506.3741.0 (auto-update did not fix) - Config: model = "gpt-5.5", model_reasoning_effort = "xhigh", windows.sandbox = "elevated"

What subscription do you have?

Pro

What platform is your computer?

Windows 11

What issue are you seeing?

Codex desktop main-process crash: RangeError: Invalid string length when loading sessions whose rollout JSONL exceeds V8's max string length

Severity

High. Crash blocks startup. The error is shown in a modal dialog that cannot be dismissed (clicking OK reopens it / Codex is unresponsive); the only way out is to kill every Codex process via Task Manager. Once a session's rollout file passes ~512 MB on disk, opening Codex hangs / crashes 100% of the time. The session — and any work in progress in it — becomes unreachable from the UI.

Environment

  • OS: Windows 11 (x64)
  • Install: Microsoft Store — C:\Program Files\WindowsApps\OpenAI.Codex_<version>_x64__2p2nqsd0c76g0\app\
  • Versions reproduced on: 26.506.2212.0 and 26.506.3741.0 (auto-update did not fix)
  • Config: model = "gpt-5.5", model_reasoning_effort = "xhigh", windows.sandbox = "elevated"

Summary

The Electron main process consumes stdout from a child process (the Codex primary runtime) and concatenates the data into a single JavaScript string in the od.write aggregator. When the cumulative string passes V8's max single-string length (~512 MB on 64-bit, 2^29 - 24 bytes for one-byte strings), String.prototype.concat / += throws RangeError: Invalid string length. The exception is uncaught in the main process, so Electron surfaces the generic "A JavaScript error occurred in the main process" dialog and the app never finishes initializing.

The trigger in practice is a session whose rollout file ~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl has grown past ~512 MB. This is easy to hit when a single thread is used heavily for image generation, because each generated image is embedded as base64 (≈1–2 MB per image) directly inline in the rollout JSONL. After ~300–500 images in one thread, the file crosses the limit.

Stack trace (verbatim from the error dialog)

Uncaught Exception:
RangeError: Invalid string length
   at od.write (C:\Program Files\WindowsApps\OpenAI.Codex_26.506.3741.0_x64__2p2nqsd0c76g0\app\resource...:7111)
   at Socket.onStdoutData (C:\Program Files\WindowsApps\OpenAI.Codex_26.506.3741.0_x64__2p2nqsd0c76g0\app\resource...:3865)
   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)

The trace path is unambiguous: Pipe.onStreamReadReadable.pushSocket.emit('data')Socket.onStdoutDataod.write. The crash is not in plugin code, model code, or rendering — it is in the stdout aggregator on the main process, attempting to grow a string past V8's limit.

Reproduction

  1. Use Codex on Windows. In a single thread, run an image-generation workflow that embeds base64 image data into the session rollout (any image-gen plugin will do — the trigger is base64 size, not the source).
  2. Continue in the same thread until C:\Users\<user>\.codex\sessions\<YYYY>\<MM>\<DD>\rollout-*.jsonl exceeds ~512 MB on disk. (In my case, ~300–500 images depending on dimensions.)
  3. Close Codex. Reopen.
  4. The "A JavaScript error occurred in the main process" dialog appears with the stack trace above. Clicking OK does nothing useful; the only way to recover is to End Task on all Codex / codex processes via Task Manager.

Reproduces 100% on my machine. I have hit it twice now in two weeks; six rollouts in my sessions/ folder grew past the limit. Sizes I observed: 506.8 MB, 786.7 MB, 963.5 MB, 1050.3 MB, 1050 MB, 1601 MB.

Expected behavior

Either:

  • Codex loads sessions of arbitrary size successfully (preferred — see fix #1 below), or
  • Codex detects the oversized rollout, refuses to load that one session with a clear message, and continues to start normally so the user retains access to all other threads.

Actual behavior

  • Main process crashes during startup with the stack above.
  • Modal error dialog cannot be dismissed.
  • Codex is fully unresponsive; multiple Codex / codex child processes remain running (in my last incident, six processes, ~3.4 GB RSS combined).
  • All threads — not just the bloated one — are inaccessible until the user manually moves the offending file out of sessions/.

Root cause analysis

V8 caps single-string length at 2^29 - 24 bytes (~512 MB) for one-byte strings, halved for two-byte strings. Reference: v8/include/v8-primitive.h kMaxLength.

The Socket.onStdoutData handler in Codex's main process accumulates the runtime subprocess's stdout into a single string buffer (visible in the trace as od.write). For sessions with rollout JSONL >512 MB, the subprocess streams the file's contents through stdout (likely as part of a "load this session" request), and the cumulative buffer overflows V8's limit.

The deeper design issue: the rollout JSONL format embeds base64 image data inline. Generated images are typically also written to ~/.codex/generated_images/, so the same bytes are duplicated in two places — but only the JSONL embed is what triggers the crash, because that's what gets pumped through stdout on session load.

Suggested fixes (in order of robustness)

  1. Stream-parse rollouts. JSONL is line-delimited; the runtime should emit one record per chunk and the main process should parse incrementally rather than buffering the entire file as one string. This eliminates the V8 limit as a constraint entirely.
  2. Cap and chunk the stdout aggregator. In od.write, when the buffer crosses some threshold (say 256 MB), flush the accumulated content to the consumer and reset, instead of growing forever. JSON message framing (length-prefixed or NDJSON) on the IPC channel would make this trivial.
  3. Catch the RangeError at the boundary and surface a useful error to the user ("Session 'X' could not be loaded — file too large, see <link>") rather than crashing the main process. This alone would unblock users and let them at least see/use other threads.
  4. Stop embedding image bytes in rollouts. Store images only in generated_images/ and reference them from the JSONL by path or content hash. Rollouts would shrink ~99% for image-gen workflows. (This is the right long-term fix; users who generate hundreds of images in one thread will keep producing >512 MB rollouts otherwise.)
  5. Add a periodic rollout-size warning in the UI ("This thread's transcript is 380 MB and may not load on next launch — start a new thread or contact support"). Soft mitigation but very cheap to add.

Workaround (for users hitting this now)

Until a fix ships, users can recover access by manually archiving the oversized rollout:

  1. End Task on all Codex / codex processes in Task Manager.
  2. In C:\Users\<user>\.codex\sessions\, locate the largest rollout-*.jsonl (sort by size). Anything >400 MB is a risk; anything >512 MB is a guaranteed crash.
  3. Move the file out of sessions/ to any other location.
  4. Edit C:\Users\<user>\.codex\session_index.jsonl and delete the line whose id matches the rollout's filename UUID. (Make a .bak first.)
  5. Reopen Codex — it should start cleanly. The archived thread is no longer in the UI but the data is preserved on disk.

Additional context

  • logs_*.sqlite in ~/.codex/ also grew to 2+ GB on my machine, contributing to slow startup but not the crash itself. Some kind of log rotation cap would be welcome.
  • Orphan ..codex-global-state.json.tmp-<timestamp>-<uuid> files accumulate in ~/.codex/ after crashes (the atomic-write tempfile is never cleaned up). Minor, but worth a sweep on startup.

What steps can reproduce the bug?

Codex desktop main-process crash: RangeError: Invalid string length when loading sessions whose rollout JSONL exceeds V8's max string length

Severity

High. Crash blocks startup. The error is shown in a modal dialog that cannot be dismissed (clicking OK reopens it / Codex is unresponsive); the only way out is to kill every Codex process via Task Manager. Once a session's rollout file passes ~512 MB on disk, opening Codex hangs / crashes 100% of the time. The session — and any work in progress in it — becomes unreachable from the UI.

Environment

  • OS: Windows 11 (x64)
  • Install: Microsoft Store — C:\Program Files\WindowsApps\OpenAI.Codex_<version>_x64__2p2nqsd0c76g0\app\
  • Versions reproduced on: 26.506.2212.0 and 26.506.3741.0 (auto-update did not fix)
  • Config: model = "gpt-5.5", model_reasoning_effort = "xhigh", windows.sandbox = "elevated"

Summary

The Electron main process consumes stdout from a child process (the Codex primary runtime) and concatenates the data into a single JavaScript string in the od.write aggregator. When the cumulative string passes V8's max single-string length (~512 MB on 64-bit, 2^29 - 24 bytes for one-byte strings), String.prototype.concat / += throws RangeError: Invalid string length. The exception is uncaught in the main process, so Electron surfaces the generic "A JavaScript error occurred in the main process" dialog and the app never finishes initializing.

The trigger in practice is a session whose rollout file ~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl has grown past ~512 MB. This is easy to hit when a single thread is used heavily for image generation, because each generated image is embedded as base64 (≈1–2 MB per image) directly inline in the rollout JSONL. After ~300–500 images in one thread, the file crosses the limit.

Stack trace (verbatim from the error dialog)

Uncaught Exception:
RangeError: Invalid string length
   at od.write (C:\Program Files\WindowsApps\OpenAI.Codex_26.506.3741.0_x64__2p2nqsd0c76g0\app\resource...:7111)
   at Socket.onStdoutData (C:\Program Files\WindowsApps\OpenAI.Codex_26.506.3741.0_x64__2p2nqsd0c76g0\app\resource...:3865)
   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)

The trace path is unambiguous: Pipe.onStreamReadReadable.pushSocket.emit('data')Socket.onStdoutDataod.write. The crash is not in plugin code, model code, or rendering — it is in the stdout aggregator on the main process, attempting to grow a string past V8's limit.

Reproduction

  1. Use Codex on Windows. In a single thread, run an image-generation workflow that embeds base64 image data into the session rollout (any image-gen plugin will do — the trigger is base64 size, not the source).
  2. Continue in the same thread until C:\Users\<user>\.codex\sessions\<YYYY>\<MM>\<DD>\rollout-*.jsonl exceeds ~512 MB on disk. (In my case, ~300–500 images depending on dimensions.)
  3. Close Codex. Reopen.
  4. The "A JavaScript error occurred in the main process" dialog appears with the stack trace above. Clicking OK does nothing useful; the only way to recover is to End Task on all Codex / codex processes via Task Manager.

Reproduces 100% on my machine. I have hit it twice now in two weeks; six rollouts in my sessions/ folder grew past the limit. Sizes I observed: 506.8 MB, 786.7 MB, 963.5 MB, 1050.3 MB, 1050 MB, 1601 MB.

Expected behavior

Either:

  • Codex loads sessions of arbitrary size successfully (preferred — see fix #1 below), or
  • Codex detects the oversized rollout, refuses to load that one session with a clear message, and continues to start normally so the user retains access to all other threads.

Actual behavior

  • Main process crashes during startup with the stack above.
  • Modal error dialog cannot be dismissed.
  • Codex is fully unresponsive; multiple Codex / codex child processes remain running (in my last incident, six processes, ~3.4 GB RSS combined).
  • All threads — not just the bloated one — are inaccessible until the user manually moves the offending file out of sessions/.

Root cause analysis

V8 caps single-string length at 2^29 - 24 bytes (~512 MB) for one-byte strings, halved for two-byte strings. Reference: v8/include/v8-primitive.h kMaxLength.

The Socket.onStdoutData handler in Codex's main process accumulates the runtime subprocess's stdout into a single string buffer (visible in the trace as od.write). For sessions with rollout JSONL >512 MB, the subprocess streams the file's contents through stdout (likely as part of a "load this session" request), and the cumulative buffer overflows V8's limit.

The deeper design issue: the rollout JSONL format embeds base64 image data inline. Generated images are typically also written to ~/.codex/generated_images/, so the same bytes are duplicated in two places — but only the JSONL embed is what triggers the crash, because that's what gets pumped through stdout on session load.

Suggested fixes (in order of robustness)

  1. Stream-parse rollouts. JSONL is line-delimited; the runtime should emit one record per chunk and the main process should parse incrementally rather than buffering the entire file as one string. This eliminates the V8 limit as a constraint entirely.
  2. Cap and chunk the stdout aggregator. In od.write, when the buffer crosses some threshold (say 256 MB), flush the accumulated content to the consumer and reset, instead of growing forever. JSON message framing (length-prefixed or NDJSON) on the IPC channel would make this trivial.
  3. Catch the RangeError at the boundary and surface a useful error to the user ("Session 'X' could not be loaded — file too large, see <link>") rather than crashing the main process. This alone would unblock users and let them at least see/use other threads.
  4. Stop embedding image bytes in rollouts. Store images only in generated_images/ and reference them from the JSONL by path or content hash. Rollouts would shrink ~99% for image-gen workflows. (This is the right long-term fix; users who generate hundreds of images in one thread will keep producing >512 MB rollouts otherwise.)
  5. Add a periodic rollout-size warning in the UI ("This thread's transcript is 380 MB and may not load on next launch — start a new thread or contact support"). Soft mitigation but very cheap to add.

Workaround (for users hitting this now)

Until a fix ships, users can recover access by manually archiving the oversized rollout:

  1. End Task on all Codex / codex processes in Task Manager.
  2. In C:\Users\<user>\.codex\sessions\, locate the largest rollout-*.jsonl (sort by size). Anything >400 MB is a risk; anything >512 MB is a guaranteed crash.
  3. Move the file out of sessions/ to any other location.
  4. Edit C:\Users\<user>\.codex\session_index.jsonl and delete the line whose id matches the rollout's filename UUID. (Make a .bak first.)
  5. Reopen Codex — it should start cleanly. The archived thread is no longer in the UI but the data is preserved on disk.

Additional context

  • logs_*.sqlite in ~/.codex/ also grew to 2+ GB on my machine, contributing to slow startup but not the crash itself. Some kind of log rotation cap would be welcome.
  • Orphan ..codex-global-state.json.tmp-<timestamp>-<uuid> files accumulate in ~/.codex/ after crashes (the atomic-write tempfile is never cleaned up). Minor, but worth a sweep on startup.

What is the expected behavior?

_No response_

Additional information

_No response_

View original on GitHub ↗

6 Comments

github-actions[bot] contributor · 2 months ago

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

  • #21232
  • #21948
  • #20544

Powered by Codex Action

rdylina · 2 months ago

Related macOS evidence: my failing thread is an 825MB rollout, but I saw a dropped 405MB IPC frame rather than RangeError. Same underlying limit class: full rollout/thread state is being aggregated into huge Electron/JS payloads. Even before a hard V8 string-length crash, the app becomes unusable; it needs size gates and paged/tail-only loading.

rdylina · 2 months ago

Follow-up / PR offer: I read the contribution docs and understand external PRs are invitation-only. If the team thinks this direction fits, I’d be happy to take a stab at an invited PR for the open-source app-server side.

Conceptually, the fix I’d propose is:

  • in no case should opening a thread load the entire transcript into app-server + client state
  • open threads from metadata + the latest tail only, roughly the most recent 100-200 messages or a small byte cap, whichever comes first
  • lazy-load older chunks only when the user scrolls upward / explicitly asks for them
  • compact or summarize the old head for model context instead of treating the full durable transcript as active state
  • keep large tool outputs/artifacts behind references, not inline in normal thread state
  • add hard byte/count limits so thread/read, thread/resume, and stream-state snapshots cannot produce 100MB+ payloads
  • document that Desktop/IDE clients should use paged turns/items and never full-hydrate a completed thread just because it was clicked

The desktop side still needs closed-code changes, but the app-server can provide the safe bounded contract and fail gracefully when a client asks for full hydration of an oversized rollout.

ohnoah · 2 months ago

I hit the same failure class on macOS Codex Desktop.

Environment

  • Codex Desktop for macOS: 26.513.31313
  • Platform: macOS
  • Affected rollout JSONL: ~/.codex/sessions/.../rollout-2026-05-15T08-21-11-019e2a82-ba83-7333-bac7-954d24293676.jsonl
  • Original rollout size: 1,368,538,366 bytes (~1.37 GB)
  • Record count: 23,442 JSONL records

Crash

Electron main-process alert:

Uncaught Exception:
RangeError: Invalid string length
at fd.write (/Applications/Codex.app/Contents/Resources/app.asar/.vite/build/workspace-root-drop-handler-D1HnRjoH.js:292:21)
at Socket.onStdoutData (/Applications/Codex.app/Contents/Resources/app.asar/.vite/build/workspace-root-drop-handler-D1HnRjoH.js:292:4023)
at Socket.emit (node:events:509: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)

Observed local behavior

  • Opening/resuming the giant session reproduced the crash.
  • thread/read returned quickly for that conversation.
  • The app-server process then grew to roughly 6-8 GB RSS.
  • I did not see a successful thread/resume completion logged for the affected conversation.
  • This looks consistent with hydrating/serializing the full thread history over stdio as one oversized JSON-RPC line/string, eventually hitting V8's string limit.

Rollout contents / workaround evidence

The bloat was mostly plain tool/result payloads, not encrypted content:

  • event_msg/mcp_tool_call_end: ~698 MB
  • response_item/function_call_output: ~648 MB
  • encrypted_content: ~3.6 MB total, max ~11 KB

A local repair that redacted only oversized plain tool-result strings and replaced oversized inline image_url data with a tiny valid data:image/png;base64,... placeholder reduced the rollout to 71,069,050 bytes. The repaired JSONL kept the same 23,442 records, had no lines over 1 MB, and preserved encrypted-content count/hash exactly.

Expected behavior: Desktop should not crash the Electron main process on a single oversized local session. It should stream/chunk large resume payloads, cap/summarize oversized tool outputs, or fail that one session with a recoverable error while keeping the rest of Desktop usable.

iqqs33i-stack · 1 month ago

Additional macOS reproduction evidence for the same failure class.

Environment

  • Codex Desktop for macOS: 26.608.12217 (CFBundleVersion=3722)
  • Bundled CLI: codex-cli 0.138.0-alpha.7
  • OS: macOS 26.4.1 build 25E253, Apple Silicon / arm64
  • Affected project: local liarsdice project
  • Affected thread/session id: 019ea61c-c900-7980-a2b6-4501d8344757
  • Affected rollout: ~/.codex/sessions/2026/06/08/rollout-2026-06-08T00-22-44-019ea61c-c900-7980-a2b6-4501d8344757.jsonl
  • Rollout size: 1,449,517,542 bytes (~1.35 GiB), 6,430 JSONL records

The thread was a long-running image asset generation/review workflow. The final goal usage reported by Codex was about 7,569,355 tokens. It generated/reviewed many image asset candidates, so the rollout grew very large.

Crash dialog

Uncaught Exception:
RangeError: Invalid string length
at jP.write (/Applications/Codex.app/Contents/Resources/app.asar/.vite/build/src-K8ZToA-n.js:368:21)
at Socket.onStdoutData (/Applications/Codex.app/Contents/Resources/app.asar/.vite/build/src-K8ZToA-n.js:368:4146)
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 observations

  • This is the same Socket.onStdoutData -> *.write path described in this issue.
  • The app logs for the same day do not contain the literal RangeError text, and no new macOS crash report was found under ~/Library/Logs/DiagnosticReports, so the visible evidence was primarily the Electron uncaught exception dialog plus the oversized rollout.
  • Relevant local app logs around the incident are under ~/Library/Logs/com.openai.codex/2026/06/11/.
  • I am not attaching full logs or the rollout because they may contain local/session content, but the repro data above should confirm that the issue still reproduces on recent macOS builds with oversized image-generation rollouts.

Expected behavior

Same as the original report: Codex Desktop should stream/chunk/cap oversized stdout/session payloads or fail only the affected thread with a recoverable error. A single oversized local rollout should not be able to crash the Electron main process.

thisislvca · 15 days ago

Adding another macOS Desktop reproduction on a newer build, with local repair measurements. This one came from Codex Desktop built-in image generation rather than Computer Use.

Environment:

  • Codex Desktop for macOS: 26.623.101652 (CFBundleVersion=4674)
  • Platform: macOS 15.7.7 (24G720), arm64
  • Failure date: 2026-07-05

Crash / log evidence:

  • Visible Electron main-process dialog:
Uncaught Exception:
RangeError: Invalid string length
at dL.write (/Applications/Codex.app/Contents/Resources/app.asar/.vite/build/src-CoIhwwHr.js:410:21)
at Socket.onStdoutData (...src-CoIhwwHr.js:418:6911)
at Socket.emit (node:events:508:28)
...
  • Local Desktop log for opening the affected thread showed a thread/read response crossing the practical V8/string boundary:
Failed to parse MCP message
errorMessage="Expected ',' or '}' after property value in JSON at position 536870710"
lineBytes=536871005
linePreview={"id":"...","result":{"thread":{"id":"..."

Affected rollout stats before repair:

rollout JSONL size:             1,165,931,575 bytes (~1.1 GB)
JSONL records:                  3,240
separate generated PNG files:   179 files / ~408 MB
image_generation result records 358
inline image result bytes:      1,138,720,392 bytes (~1.09 GiB)

Important duplication detail: each generated image was present once as event_msg / image_generation_end.result and once as response_item / image_generation_call.result, while also being saved as a PNG artifact under the local generated-images directory.

Local recovery that made the rollout small and parseable again:

  1. Backed up the original rollout.
  2. Replaced payload.result for image_generation_end / image_generation_call with a small valid 64x64 PNG base64 string, not an arbitrary text placeholder.
  3. Converted 17 old function_call_output.output values containing input_image / data:image content into text-only omission strings.
  4. Preserved prompts, call ids, timestamps, saved_path, and normal text records.
  5. Re-validated JSONL.

After repair:

rollout JSONL size:             6,096,679 bytes (~5.8 MB)
JSONL records:                  3,240
max JSONL line:                 84,047 bytes
raw data:image records:         0
image_generation result records 358
invalid image result records:   0

This supports the same root cause already described here: Desktop/app-server should not build full thread/read / resume / stream-state payloads that include historical image bytes, and rollout persistence should not store generated image bytes inline and duplicated across event and response surfaces. A built-in repair path that externalizes or schema-safely omits old image payloads would have recovered this thread without manual JSONL surgery.