Codex App white-screens on startup after restoring a malformed new-conversation composer draft
What version of the Codex App are you using (From “About Codex” dialog)?
Codex App 26.602.71036 (CFBundleVersion 3685) Bundled CLI observed locally: codex-cli 0.137.0-alpha.4
What subscription do you have?
Pro $100
What platform is your computer?
Darwin 25.5.0 arm64 arm macOS 26.5.1 (25F80)
What issue are you seeing?
Pasting a small malformed JSON/Python-like text into a new Codex App conversation can freeze the renderer. After force-quitting and reopening Codex App, the splash/logo finishes and the app shows a blank white screen. In the broken state, a renderer process was consuming approximately one full CPU core.
The app remained white-screened across restarts until I manually removed the persisted new-conversation composer draft from Codex UI state and cleared Electron Local Storage.
The bad persisted state was:
~/.codex/.codex-global-state.json- key path:
electron-persisted-atom-state.composer-prompt-drafts-v1.new-conversation - type: string
- length: 557 chars
- value: exactly the first line of the pasted 3-line prompt, ending mid-object after
'dim': 'jobType',
This is notable because the full input was only about 1.4 KB. The persisted draft was not large; it was a partially written, syntactically incomplete text value.
What steps can reproduce the bug?
- Open Codex App on macOS.
- Start a new local conversation.
- Paste the following malformed text into the composer. This is a redacted version that preserves the nested escaping and structure of the original prompt:
JD画像:[{'emotion': 'clearPos', 'useReasoning': False, 'values': '\"[\"园艺\"]\"', 'dim': 'major', 'text': '园艺学', 'llmText': '园艺学'}, {'emotion': 'clearPos', 'useReasoning': False, 'values': '\"[{\"country\": \"<country>\", \"district_name\": \"<district>\", \"city_name\": \"<city>\", \"province\": \"<province>\", \"city\": \"<city_code>\", \"district\": \"<district_code>\", \"province_name\": \"<province_name>\"}]\"', 'dim': 'region', 'text': '<district>', 'llmText': '<city><district>'}, {'emotion': 'clearPos', 'useReasoning': False, 'values': '\"[\"{\"text\":\"园艺师\",\"jobType\":\"<job_type_id>\"}\"]\"', 'dim': 'jobType',
'remain_text': '高级', 'text': '园艺师', 'llmText': '高级园艺师、园艺师'}, {'emotion': 'clearPos', 'useReasoning': False, 'values': '\"[]\"', 'dim': 'industry', 'text': '保险行业', 'llmText': '保险'}, {'emotion': 'clearPos', 'useReasoning': False, 'values': '\"[\"全职\"]\"', 'dim': 'keyword', 'text': '全职', 'llmText': '全职'}, {'emotion': 'clearPos', 'useReasoning': False, 'values': '[\"20,-1\"]', 'dim': 'age', 'text': '20岁以上', 'llmText': '20岁以上的'}, {'emotion': 'clearPos', 'useReasoning': False, 'values': ['-1'], 'dim': 'desiredSalary', 'text': '薪资不限', 'llmText': '期望薪资放宽到不限'},
{'emotion': 'vaguePos', 'useReasoning': False, 'values': '\"[\"领导经验\"]\"', 'dim': 'keyword', 'text': '领导经验', 'llmText': '有一定的领导经验'}]
- The app freezes during or immediately after the paste.
- Force quit Codex App.
- Reopen Codex App.
- The logo/splash finishes, then the main window stays blank white.
Observed persisted draft after the failed paste:
{
"path": "~/.codex/.codex-global-state.json",
"key": "electron-persisted-atom-state.composer-prompt-drafts-v1.new-conversation",
"type": "string",
"length": 557,
"equals_first_line_of_original_prompt": true,
"tail": "..., 'values': '\\\"[\\\"{\\\"text\\\":\\\"园艺师\\\",\\\"jobType\\\":\\\"<job_type_id>\\\"}\\\"]\\\"', 'dim': 'jobType', "
}
What is the expected behavior?
Pasting arbitrary plain text into the composer should not hang the renderer, even if the text looks like malformed JSON or Python literals.
If a composer draft is partially written or cannot be safely restored, startup should still render the app. The app should ignore, clear, or quarantine the bad draft and surface a recoverable error rather than permanently white-screening on every restart.
Additional information
Recovery workaround
The app recovered after clearing the bad persisted draft from both host-global state and Electron Local Storage:
- Remove
electron-persisted-atom-state.composer-prompt-drafts-v1.new-conversationfrom~/.codex/.codex-global-state.json. - Remove the same entry from
~/.codex/.codex-global-state.json.bakif present. - Clear the cached copy under
~/Library/Application Support/Codex/Local Storage/leveldb. - Restart Codex App.
Deleting the broken conversation/thread alone was not sufficient because the startup failure was tied to the top-level persisted UI draft state, not just conversation history.
Local code/source investigation
I downloaded the current Codex source zip and could inspect the open app-server/thread-store code, but I could not find the desktop Electron renderer/main source for persisted atoms and composer drafts in that zip. The relevant Electron UI behavior below comes from inspecting the packaged app bundles.
Packaged Electron main bundle behavior observed locally:
sendPersistedAtomState(e) {
let t = this.globalState.get(`electron-persisted-atom-state`) ?? {};
this.sendMessageToView(e, { type: `persisted-atom-sync`, state: t });
}
updatePersistedAtomState(e, t) {
let n = { ...this.globalState.get(`electron-persisted-atom-state`) ?? {} };
t === void 0 ? delete n[e] : n[e] = t;
this.globalState.set(`electron-persisted-atom-state`, n);
this.broadcastPersistedAtomUpdate(e, t);
}
The persisted atom map appears to be restored and sent to the renderer as an arbitrary record, without per-atom validation for composer-prompt-drafts-v1.
Packaged renderer bundle behavior observed locally:
- Renderer requests
persisted-atom-sync-requestduring startup. - Host replies with
persisted-atom-sync. - Composer uses
new-conversationas the fallback key for drafts when there is no active conversation id. - The local atom storage falls back to
window.localStoragewithJSON.parse/JSON.stringify, which explains why the stale draft could also survive in Electron Local Storage.
Why this looks like an app bug
A single corrupted or partially persisted composer draft should not be able to make Codex App unrecoverable across restarts. At minimum, the app could:
- Validate or sanitize persisted composer draft values before hydration.
- Wrap composer draft restoration so failures clear only that draft atom.
- Add a startup safe mode or automatic quarantine for persisted UI state that causes renderer startup failure.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗