openai / openai/codex

[Windows] Composer autosave triggers repeated full rewrites of .codex-global-state.json and .bak, causing severe SSD write amplification

Open
#43,393 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug performance windows-os
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

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

Codex Desktop for Windows: OpenAI.Codex_26.901.6511.0_x64__2p2nqsd0c76g0

What subscription do you have?

Typing or editing text in the Codex composer causes repeated full rewrites of the entire .codex-global-state.json and .codex-global-state.json.bak files. The actual composer draft mutation is very small (usually tens or hundreds of bytes), but each autosave triggers an atomic rewrite of approximately 1.25 MB for the primary global-state file and another approximately 1.25 MB for the backup. This causes significant SSD write amplification during normal composer editing. The issue is specifically correlated with electron-persisted-atom-state -> composer-prompt-drafts-v2. When the composer is idle, the writes stop. When Codex is running/inferencing but the composer is not being edited, the draft watcher also remains idle.

What platform is your computer?

Windows 11 x64

What issue are you seeing?
What version of the Codex App are you using?

Codex Desktop for Windows:

OpenAI.Codex_26.901.6511.0_x64__2p2nqsd0c76g0

The Electron frontend process is:

ChatGPT.exe

The app-server process is:

codex.exe

What platform is your computer?

Windows 11 x64.

What issue are you seeing?

Typing or editing text in the Codex composer causes repeated full rewrites of the entire .codex-global-state.json and .codex-global-state.json.bak files.

The actual composer draft mutation is very small (usually tens or hundreds of bytes), but each autosave triggers an atomic rewrite of approximately 1.25 MB for the primary global-state file and another approximately 1.25 MB for the backup.

This causes significant SSD write amplification during normal composer editing.

The issue is specifically correlated with electron-persisted-atom-state -> composer-prompt-drafts-v2.

When the composer is idle, the writes stop.

When Codex is running/inferencing but the composer is not being edited, the draft watcher also remains idle.

Reproduction
  1. Start Codex Desktop on Windows.

  2. Open an existing thread or a new thread.

  3. Monitor:

    C:\Users\<user>\.codex\.codex-global-state.json*

    with Sysinternals Process Monitor.

  4. Leave the composer idle for several minutes.

    Result:

    • no repeated global-state writes
    • no composer-prompt-drafts-v2 mutations
  5. Start typing normally in the composer.

  6. Observe repeated mutations to:

    electron-persisted-atom-state -> composer-prompt-drafts-v2

  7. At the same timestamps, ProcMon shows repeated full writes to temporary files such as:

    ..codex-global-state.json.tmp-<uuid>

    and

    ..codex-global-state.json.bak.tmp-<uuid>

  8. Each temporary file is then atomically renamed/replaced into:

    .codex-global-state.json

    or

    .codex-global-state.json.bak

  9. Stop typing.

    Result:

    • the draft mutations stop
    • the repeated global-state rewrites stop
ProcMon evidence

Each global-state serialization is approximately:

  • WriteFile: 524,288 bytes
  • WriteFile: 524,288 bytes
  • WriteFile: ~207,000 bytes

Total per temporary file:

~1,256,000 bytes

The same state is written separately to:

  • .codex-global-state.json
  • .codex-global-state.json.bak

So one logical autosave can result in approximately:

~2.5 MB of file writes

even though the underlying draft mutation may only be a few bytes.

Draft mutation evidence

During normal typing, the composer-prompt-drafts-v2 state changes every ~1–few seconds.

Example:

  • 11 -> 84 bytes
  • 84 -> 81 bytes
  • 81 -> 82 bytes
  • 82 -> 84 bytes
  • 84 -> 88 bytes
  • 88 -> 150 bytes

The same draft content is also mirrored under two keys:

  • client-new-thread:<id>
  • local:<thread-id>

Both entries change in sync.

The important point is that a very small composer-state mutation causes a full ~1.25 MB global-state rewrite plus another ~1.25 MB backup rewrite.

A/B test: typing vs paste

Typing a short prompt manually produced approximately:

25,131,020 bytes

of writes to the global-state temporary files.

Pasting a much larger prompt in one operation and sending it produced approximately:

13,829,857 bytes

of writes.

The paste test caused only a few draft-state transitions:

  • clear old draft
  • set pasted draft
  • clear after submit

This strongly suggests that write volume is driven by composer edit/autosave frequency rather than prompt size.

Idle / inference behavior

When the composer is idle:

  • ProcMon shows no repeated global-state rewrite activity.
  • composer-prompt-drafts-v2 does not change.

When Codex is executing/inferencing and the composer is not being edited:

  • the draft watcher remains idle.

This suggests that the repeated writes are caused by composer draft persistence rather than model inference, MCP activity, or background heartbeat activity.

Additional observation: atomic rename failures

Most SetRenameInformationFile operations succeed.

However, occasional operations return:

ACCESS DENIED

after which Codex appears to clean up the temporary file.

This is secondary to the main problem: the primary issue is the high frequency of full-file rewrites.

Global-state size

Before investigation, .codex-global-state.json was approximately:

3.14 MB

A large prompt-history field (~1.35 MB) was removed as a diagnostic test, reducing the file to approximately:

1.19 MB

This substantially reduced the size of each rewrite, but did not remove the underlying behavior.

The composer autosave still rewrites the entire global-state file and backup.

Expected behavior

Saving composer drafts should not require repeatedly rewriting the entire global-state file and its full backup.

Possible approaches:

  • debounce composer persistence more aggressively
  • persist drafts only after user inactivity
  • save on blur/thread switch/submit/shutdown
  • store composer drafts in a separate small file/database
  • update only the changed state instead of serializing the full global-state payload
  • avoid rewriting .bak on every small composer-state mutation
Actual behavior

Small composer edits trigger repeated full atomic rewrites of both:

  • .codex-global-state.json
  • .codex-global-state.json.bak

This produces unnecessary write amplification and avoidable SSD wear.

Why this matters

This is not just normal chat/session persistence.

The observed write amplification is caused by the mismatch between:

  • very small composer-prompt-drafts-v2 mutations
  • full ~1.25 MB primary rewrite
  • full ~1.25 MB backup rewrite

Long editing sessions in the composer can therefore generate tens or hundreds of MB of writes from very small text edits.

Related issues

There are other reports of excessive Codex Desktop disk writes and repeated JSON rewrites, for example:

  • #32496 - excessive SSD write churn from small JSON files
  • #26401 - global-state cleanup/repopulation behavior
  • #41081 - composer/send path still updates global-state and WAL files on Windows
  • #38757 - .codex-global-state.json / .bak durability concerns

This report appears to describe a more specific trigger:

composer draft autosave -> full global-state + backup rewrite

What steps can reproduce the bug?
  1. Start Codex Desktop on Windows.

  2. Open an existing thread or a new thread.

  3. Monitor:

    C:\Users\<user>\.codex\.codex-global-state.json*

    with Sysinternals Process Monitor.

  4. Leave the composer idle for several minutes.

    Result:

    • no repeated global-state writes
    • no composer-prompt-drafts-v2 mutations
  5. Start typing normally in the composer.

  6. Observe repeated mutations to:

    electron-persisted-atom-state -> composer-prompt-drafts-v2

  7. At the same timestamps, ProcMon shows repeated full writes to temporary files such as:

    ..codex-global-state.json.tmp-<uuid>

    and

    ..codex-global-state.json.bak.tmp-<uuid>

  8. Each temporary file is then atomically renamed/replaced into:

    .codex-global-state.json

    or

    .codex-global-state.json.bak

  9. Stop typing.

    Result:

    • the draft mutations stop
    • the repeated global-state rewrites stop
What is the expected behavior?

No response

Additional information

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing persistence for electron-persisted-atom-state -> composer-prompt-drafts-v2 and the .codex-global-state.json* files described in the report. Reproduce typing and idle behavior with Sysinternals Process Monitor, then identify why each small draft mutation rewrites both full files. Done means composer editing no longer causes repeated full primary and backup rewrites while draft persistence remains functional.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron
Domain
desktop, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.