Windows Desktop local state is not crash-safe after power loss: pins/projects reset, config regresses, future timestamps

Open 💬 20 comments Opened Jun 8, 2026 by dudantas
💡 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)?

Codex Desktop Windows app package:

OpenAI.Codex_26.602.4764.0_x64__2p2nqsd0c76g0
Version: 26.602.4764.0

The app was updated before filing this report.

Additional local CLI present on the same machine:

codex-cli 0.125.0

What subscription do you have?

ChatGPT Pro

What platform is your computer?

Microsoft Windows 11 Pro
Version: 10.0.26200
Build: 26200
Architecture: x64
Filesystem: NTFS

What issue are you seeing?

After power loss / abrupt shutdown, Codex Desktop local state is repeatedly coming back in a partially reset or internally inconsistent state.

The user-visible symptoms are:

  • pinned chats disappear from the sidebar;
  • saved projects/workspaces disappear or collapse to a single workspace;
  • some project views show No chats even though local conversations still exist;
  • the TUI/skills refresh can fail because config.toml regresses to an invalid service_tier = "default" value for this installed version;
  • local metadata can get a far-future timestamp (7026), making an old session look newer than real recent sessions.

This has happened multiple times after power loss. It looks like the local Desktop/profile state under %USERPROFILE%\.codex is not crash-safe across abrupt shutdowns. The underlying conversations are generally still recoverable from local SQLite/session files, but the UI state and indexes become misleading or broken.

Concrete local evidence

The affected local state directory is %USERPROFILE%\.codex.

1. .codex-global-state.json was rewritten to a minimal state

Before repair, the current .codex-global-state.json had effectively collapsed to one workspace:

file: %USERPROFILE%\.codex\.codex-global-state.pre-repair-20260606-070944.json
size: 2,053 bytes
pinned-thread-ids: 1
project-order: 1
electron-saved-workspace-roots: 1
thread-workspace-root-hints: 0
active-workspace-roots: [%USERPROFILE%]

After reconstructing from a known-good backup and state_5.sqlite, the repaired state contained:

file: %USERPROFILE%\.codex\.codex-global-state.backup-20260606-070944.repaired.json
size: 102,112 bytes
pinned-thread-ids: 11
project-order: 27
electron-saved-workspace-roots: 27
thread-workspace-root-hints: 675

Current post-repair state, after reopening the app, remains healthy:

pinned-thread-ids: 13
project-order: 28
electron-saved-workspace-roots: 27
thread-workspace-root-hints: 675
example project-specific hints: 3 for a project that previously showed `No chats`

The repaired JSON is UTF-8 without BOM. The first bytes are:

123,13,10
2. config.toml regressed to service_tier = "default"

The broken config contained this at the top:

model = "gpt-5.5"
model_reasoning_effort = "xhigh"
personality = "pragmatic"
sandbox_mode = "danger-full-access"
approval_policy = "never"
service_tier = "default"

The local error was:

failed to refresh skills: skills/list failed in TUI: skills/list failed: failed to reload config: C:\Users\<USER>\.codex\config.toml:6:16: unknown variant `default`, expected `fast` or `flex`

Restoring a previous fixed backup of config.toml removed the parse error. The current restored file no longer contains service_tier = "default".

3. A local thread and version cache jumped to year 7026

Before repair, one local thread had:

id: 911d47ec-a3a4-7503-b8ae-3f47cecabbd4
cwd: <redacted project path>
updated_utc: 7026-02-04 18:32:49
updated_ms: 159554946769000
rollout_path: %USERPROFILE%\.codex\sessions\7026\02\04\rollout-7026-02-04T15-32-49-911d47ec-a3a4-7503-b8ae-3f47cecabbd4.jsonl

version.json also had a last_checked_at timestamp in 7026:

{
  "latest_version": "0.96.0",
  "last_checked_at": "7026-02-04T18:32:48.404538600Z",
  "dismissed_version": null
}

The 7026 session file contained only 4 metadata/instruction lines, not meaningful conversation content. After backing up the database and file, I moved it to the matching 2026 path and updated the SQLite row to a 2026 timestamp.

Post-repair validation:

future files under %USERPROFILE%\.codex: 0
future threads in state_5.sqlite: 0
4. Local conversation data was still present

The state database still contained many threads:

state_5.sqlite total threads: 791
active threads: 714

This looks like a local state/index/UI consistency problem rather than complete data deletion.

What steps can reproduce the bug?

This is tied to abrupt shutdown/power loss rather than a deterministic in-app action, but the observed sequence is:

  1. Use Codex Desktop on Windows with multiple saved workspaces/projects, pinned chats, and many local conversations.
  2. Leave Codex Desktop open with app-server/background processes active.
  3. Machine loses power or is abruptly shut down.
  4. Restart Windows and open Codex Desktop.
  5. Observe that the sidebar has lost pins and saved projects, or project views show No chats.
  6. In some cases, launch TUI/CLI or trigger skills refresh and observe config parsing failure from service_tier = "default".
  7. Inspect %USERPROFILE%\.codex and compare:
  • .codex-global-state.json
  • state_5.sqlite
  • session_index.jsonl
  • sessions/**/rollout-*.jsonl
  • version.json

The conversations remain mostly recoverable locally, but the UI state and metadata can be corrupted or reset.

What is the expected behavior?

Codex Desktop should make local state persistence crash-safe across abrupt shutdowns.

Specifically:

  • .codex-global-state.json should not be replaced by a minimal state after a crash if recoverable state exists.
  • Writes to local JSON state should be atomic, for example write temp file, fsync, then rename.
  • config.toml should not regress to a value that the installed parser rejects.
  • Timestamps should be validated or clamped; a local session/version cache should not jump to year 7026.
  • The app should detect drift between .codex-global-state.json, state_5.sqlite, session_index.jsonl, and existing rollout files.
  • If project state is incomplete, Codex should expose a supported local repair/reindex command rather than making users inspect and patch SQLite/JSON manually.
  • Project views should not display No chats when matching active local threads exist in state_5.sqlite and rollout files exist on disk.

Actual behavior

After power loss, Codex Desktop can reopen with:

  • pins reduced from 11+ to 1;
  • saved projects reduced from dozens to 1;
  • workspace hints removed;
  • project chat views empty despite active rows in state_5.sqlite;
  • config.toml containing service_tier = "default", which this install rejects;
  • local metadata containing future year 7026.

Workaround used locally

With Codex Desktop closed:

  1. Back up the broken files/databases.
  2. Restore a known-good config.toml without service_tier = "default".
  3. Rebuild .codex-global-state.json from a known-good backup and active rows in state_5.sqlite.
  4. Preserve and restore:
  • pinned-thread-ids
  • project-order
  • electron-saved-workspace-roots
  • active-workspace-roots
  • thread-workspace-root-hints
  1. Write JSON as UTF-8 without BOM.
  2. Move the sessions\7026\... rollout to the equivalent sessions\2026\... path and update the corresponding SQLite row.
  3. Restart Codex Desktop.

After that, pins/projects/project hints were visible again.

Related issues

This overlaps with several existing local state/sidebar/path reports, but I did not find an exact duplicate for the power-loss/crash-safety combination:

  • #17540: Windows app older local threads disappear from sidebar/search while still present on disk.
  • #24178: conversations become empty and resume fails with C:\... vs \\?\C:\... stale path.
  • #25500: project sidebar shows No chats for projects with older non-archived conversations.
  • #22452: local thread hidden/stale when state_5.sqlite and session_index.jsonl drift.
  • #24425: corrupted session JSON/history after crash; maintainer asked about power loss in discussion.

The distinct parts here are:

  • reproducible trigger is power loss / abrupt shutdown;
  • .codex-global-state.json collapses to a minimal state;
  • config.toml regresses to an invalid service_tier = "default" for this version;
  • a local thread and version cache jumped to year 7026;
  • the same local profile needed coordinated repair across global state JSON, config TOML, SQLite, session index, and session paths.

Additional information

No raw logs_2.sqlite, auth.json, full config.toml, or rollout content is attached publicly because those files may contain private prompts, local project names, tokens, URLs, or account data.

I can provide redacted diagnostics or specific SQL/query output if maintainers say which fields are useful and safe to share.

View original on GitHub ↗

20 Comments

github-actions[bot] contributor · 2 months ago

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

  • #26421

Powered by Codex Action

dudantas · 2 months ago
Potential duplicates detected. Please review them and close your issue if it is a duplicate. * Desktop: config.toml zero-filled to NUL bytes after ungraceful shutdown — non-atomic write on Windows loses entire file content #26421 _Powered by Codex Action_

I agree this is related to #26421, especially the non-atomic persistence / power-loss angle.

I do not think this is an exact duplicate, though. #26421 focuses on config.toml becoming zero-filled with NUL bytes after an ungraceful shutdown. In this case, config.toml was still readable but regressed to service_tier = "default", while .codex-global-state.json was rewritten to a minimal state and lost pins/projects/workspace hints. The same incident also produced a future-year 7026 timestamp in version.json, a session path, and state_5.sqlite.

So the shared root cause may be crash-unsafe local persistence, but the affected state is broader than config.toml: .codex-global-state.json, session metadata, SQLite timestamps, and project/sidebar recovery are involved too.

dudantas · 2 months ago

Additional update: repeated sidebar loss after recovery + destructive Windows hang dialog

I have continued investigating this locally after the original report. The behavior still looks broader than a single config.toml corruption case.

Installed version still affected

Current installed Windows package on the affected machine:

OpenAI.Codex 26.602.4764.0
PackageFullName: OpenAI.Codex_26.602.4764.0_x64__2p2nqsd0c76g0

Chats can still exist locally while disappearing from the UI

After repairing .codex-global-state.json and recovering old projects/pins, I was able to make old project chats visible again. However, some old project chats can disappear from the sidebar again later.

The important detail is that this does not appear to be complete conversation deletion. Local rows and rollout/session files still exist. The problem appears to be that the Desktop sidebar/project list does not hydrate all local conversations from durable storage. It seems to depend on a combination of:

  • .codex-global-state.json
  • project-order
  • electron-saved-workspace-roots
  • thread-workspace-root-hints
  • pinned-thread-ids
  • recent thread pagination / in-memory hydration
  • state_5.sqlite thread metadata

A temporary workaround was to add missing thread ids to pinned-thread-ids. That forced the Desktop app to hydrate them. After unpinning, those chats moved back under their correct projects based on their workspace metadata. This strongly suggests pinning is acting as a hydration trigger, not as the real source of project membership.

Local SQLite evidence

The affected profile currently has many active local threads whose thread_source is still NULL:

active threads: 719
active threads with thread_source IS NULL: 463
active threads with thread_source = 'user': 256

Several older projects have only or mostly thread_source IS NULL active rows. These are the same kinds of projects that are more likely to show as empty or incomplete in the Desktop sidebar even though matching conversations exist in state_5.sqlite and rollout files.

This may be a migration/indexing issue where older Desktop-created local conversations are still present but no longer pass the current sidebar/recent-thread filters unless they are pinned or otherwise explicitly hydrated.

Additional Windows-specific destructive failure mode

There is another Windows-specific way the crash/hang becomes destructive.

When Codex Desktop freezes while I am typing in the composer, Windows shows the standard Codex is not responding dialog. The focused/default button is End Process. If I press Enter at that exact moment intending to submit my prompt, Windows terminates Codex instead.

Impact:

  • composer draft can be lost
  • pasted/uploading attachments can be lost
  • running threads can be interrupted
  • local state corruption/recovery problems become more likely after forced termination

This is worse than a normal app hang because the default focused Windows action can terminate the app accidentally during normal typing. I have a screenshot of this state, but the raw screenshot contains private workspace paths and prompt content, so I am not attaching the unredacted image publicly.

Relation to newer reports

This also seems related to #27175, although this report predates it and I am currently on 26.602.4764.0. The overlapping symptoms are:

  • project chats disappearing or showing empty
  • local state/sidebar/index handling problems
  • queue/running work being fragile when Desktop crashes
  • clearing session files not necessarily fixing the Desktop UI state

What would help

In addition to atomic/crash-safe file writes, I think the Desktop app needs a supported recovery/reindex path for local state, for example:

  • validate and repair .codex-global-state.json from state_5.sqlite + existing rollout files
  • rehydrate project sidebar entries from active local threads by workspace root
  • migrate old local thread_source IS NULL Desktop threads to the current expected source kind, or include them in local recovery/search
  • avoid relying on pins as the only practical way to hydrate old local project threads
  • persist composer drafts and attachment state before any UI-blocking operation
  • on startup after forced termination, detect incomplete state writes and offer recovery instead of silently showing empty projects

The current workaround requires manually inspecting and patching JSON/SQLite/session files, which is risky and not something users should have to do.

dudantas · 2 months ago

Additional update: GUI freeze improved after local log/plugin cleanup

I have one more data point from the same affected Windows profile. The Desktop app later started visually freezing / becoming unresponsive for many seconds, sometimes close to a minute, without a clear Windows Application Hang event being recorded.

During one live freeze/startup investigation on the updated Windows app package:

OpenAI.Codex_26.608.1337.0_x64__2p2nqsd0c76g0

I observed:

  • Codex.exe was still marked as responding by Windows, but it was temporarily consuming high CPU and significant disk reads.
  • %USERPROFILE%\.codex\logs_2.sqlite was about 668 MB.
  • %USERPROFILE%\.codex\logs_2.sqlite-wal had grown to about 186 MB.
  • Simple local log aggregation queries were very slow / timed out during earlier inspection.
  • The log repeatedly contained:
failed to load plugin: plugin is not installed plugin="browser-use@openai-bundled"

The local config.toml still had browser-use@openai-bundled enabled, but the local bundled plugin cache only contained browser, chrome, and computer-use, not browser-use.

A conservative mitigation helped noticeably:

  1. Closed Codex Desktop.
  2. Archived only diagnostic log files:
  • %USERPROFILE%\.codex\logs_2.sqlite
  • %USERPROFILE%\.codex\logs_2.sqlite-wal
  • %USERPROFILE%\.codex\logs_2.sqlite-shm
  • %USERPROFILE%\.codex\log\codex-tui.log
  1. Disabled the missing browser-use@openai-bundled plugin entry in config.toml.
  2. Restarted Codex Desktop.

After that, the app appeared noticeably more responsive.

I did not touch chat/session state during this mitigation, specifically not:

  • state_5.sqlite*
  • sessions/
  • archived_sessions/
  • session_index.jsonl
  • history.jsonl
  • .codex-global-state.json

This suggests there may be a second contributing factor beyond the crash-safety/sidebar recovery problem: a large local log database plus repeated loading attempts for a configured-but-missing bundled plugin can make the Desktop app appear frozen or slow, and that freeze can lead to the destructive Windows End Process flow described above.

Follow-up: I later hit a stronger version of the same failure on 26.609.4994.0: a Crashpad dump was produced, the app restarted/updated from logs referencing 26.609.3341.0, app-server initialization hit restart-required and took about 127 seconds to recover, and active chats/tasks stopped. I added the concrete timeline and log snippets in a newer comment below.

dudantas · 2 months ago

Additional update: Desktop crash/restart while active chats were running

This happened again on the same Windows profile, this time after the app had updated to:

OpenAI.Codex_26.609.4994.0_x64__2p2nqsd0c76g0
Version: 26.609.4994.0

From the UI side, it initially looked like Codex was still running but the GUI was not becoming visible. The process list showed Codex.exe processes still alive, including one process with a valid main window handle. After the UI came back, the active chats/tasks had stopped and I had to restart the work.

Local evidence suggests this was not just an off-screen or minimized window:

  • Previous desktop log paths reference OpenAI.Codex_26.609.3341.0.
  • A Crashpad dump was created at 2026-06-15 10:04:50 local time:

%LOCALAPPDATA%\Packages\OpenAI.Codex_2p2nqsd0c76g0\LocalCache\Roaming\Codex\web\Codex\Crashpad\reports\efe6e255-9215-4de3-a655-dcdb4c3361e6.dmp

  • The new Codex.exe process started at 2026-06-15 10:04:56 local time.
  • The new desktop log shows app-server startup at 2026-06-15T13:04:58Z.
  • Initialization hit initialize_handshake_timeout with connectionError={"code":"restart-required"}.
  • App-server initialization only completed around 2026-06-15T13:07:07Z, after about 127 seconds.

The old pre-restart log had repeated messages immediately before the crash/restart window:

Failed to register chat process notification
Unexpected token '\^@' ... is not valid JSON
Item not found in turn state

There were also in-app browser/sidebar errors around the same period, including Cannot find context with specified id and ERR_FAILED (-2) loading 'about:blank', followed by browser sidebar page teardown.

I am not attaching the raw dump or full logs publicly because they may contain local paths, conversation ids, and other sensitive data. The important symptom is that Codex can appear to still have processes alive, then restart/update underneath active work, and the active chats/tasks are lost or interrupted. This seems to reinforce the need for crash-safe session/process recovery, not only sidebar reindexing.

safal207 · 2 months ago

This issue maps closely to LS work on durable trajectories, crash-safe persistence, integrity evidence, and deterministic recovery.

A useful distinction is:

state file exists
≠ state is complete
≠ indexes agree
≠ the newest timestamp is trustworthy

A crash-safe local state model could treat SQLite, rollout files, session indexes, configuration, and UI projections as separate materializations of one durable trajectory.

authoritative event history
→ thread registry
→ workspace index
→ sidebar/project projection
→ cached configuration state

The central invariant would be:

«After an abrupt shutdown, Codex should recover from the newest internally consistent committed state—not from whichever file happens to have the latest modification time.»

A possible checkpoint manifest:

{
"schema_version": "local-state-checkpoint/v0.1",
"checkpoint_id": "checkpoint-20260624-1842",
"created_at": "2026-06-24T18:42:11Z",
"generation": 1842,
"components": {
"state_db": {
"path": "state_5.sqlite",
"digest": "sha256:...",
"transaction_sequence": 91842
},
"global_state": {
"path": ".codex-global-state.json",
"digest": "sha256:...",
"generation": 1842
},
"session_index": {
"path": "session_index.jsonl",
"digest": "sha256:...",
"last_sequence": 7712
},
"rollout_registry": {
"count": 791,
"digest": "sha256:..."
}
},
"commit_status": "COMMITTED"
}

A safe write sequence could be:

write temporary file
→ flush file
→ fsync
→ validate parse and schema
→ atomic rename
→ fsync parent directory
→ update checkpoint manifest

The runtime should never replace a rich valid projection with a minimal default merely because startup encountered an incomplete write.

A recovery process could be:

discover candidate states
→ reject incomplete/uncommitted generations
→ validate digests and schemas
→ compare registries
→ detect impossible timestamps
→ reconstruct derived indexes
→ publish recovery report

A structured drift report might look like:

{
"schema_version": "state-reconciliation-report/v0.1",
"checkpoint_id": "checkpoint-20260624-1842",
"status": "DRIFT_DETECTED",
"findings": [
{
"component": ".codex-global-state.json",
"finding": "PROJECTION_INCOMPLETE",
"observed_projects": 1,
"recoverable_projects": 27
},
{
"component": "session_index.jsonl",
"finding": "MISSING_THREAD_REFERENCES",
"count": 675
},
{
"component": "thread timestamp",
"finding": "IMPOSSIBLE_FUTURE_TIME",
"observed": "7026-02-04T18:32:49Z"
}
],
"decision": "REBUILD_DERIVED_PROJECTIONS"
}

Another useful invariant:

«UI indexes and sidebar state should be treated as rebuildable projections, not as the sole source of truth for whether conversations exist.»

For example:

project view says “No chats”

  • active thread rows exist
  • matching rollout files exist

→ projection is stale
→ rebuild index

not:

“No chats”
→ assume conversations were deleted

Timestamp handling should also be evidence-based. A future timestamp should not automatically win conflict resolution.

timestamp > current time + allowed skew
→ INVALID_TIME_EVIDENCE
→ exclude from freshness ordering

Configuration recovery should be version-bound:

{
"config_snapshot": {
"schema_version": 7,
"writer_version": "26.602.4764.0",
"parser_compatibility": [
"26.602.x"
],
"digest": "sha256:..."
}
}

This helps prevent an older or partial writer from restoring a value the installed parser cannot accept.

A deterministic conformance fixture could test:

  1. process terminates during JSON write;
  2. power loss occurs before atomic rename;
  3. power loss occurs after rename but before checkpoint commit;
  4. SQLite contains threads missing from the UI projection;
  5. rollout file exists without a matching registry row;
  6. config snapshot contains a value unsupported by the current parser;
  7. timestamp is thousands of years in the future;
  8. newest file is corrupt but the previous generation is valid;
  9. recovery rebuilds projects and pins without duplicating threads;
  10. running recovery twice produces the same result.

A supported command such as:

codex doctor --reconcile-state

could generate a redacted report first, then apply only deterministic repairs:

SCAN
→ REPORT
→ BACKUP
→ REPAIR
→ VERIFY
→ COMMIT

Related LS work:

  • causal audit:

https://github.com/safal207/LS/issues/594

  • evidence gates:

https://github.com/safal207/LS/issues/595

  • deterministic replay:

https://github.com/safal207/LS/issues/597

  • recovered continuation state:

https://github.com/safal207/LS/pull/651

Would a small vendor-neutral "StateCheckpointManifest" and crash-recovery conformance fixture help test atomic persistence, cross-store drift detection, timestamp validation, and deterministic projection rebuilding?

dudantas · 1 month ago

July 14-15 recurrence on 26.707.8479.0: UI hang, abrupt restart, and persistent NUL process cache

This happened again on the same Windows profile with a much newer package:

OpenAI.Codex_26.707.8479.0_x64__2p2nqsd0c76g0

This recurrence was not caused by a power loss. The Desktop UI became unresponsive while tasks were running and Windows displayed the standard "not responding" flow. While its confirmation prompt had focus, a stray Space/Enter keypress intended for the composer confirmed End Process. A single accidental keypress therefore terminated the app and stopped the running turns; the tasks had to be resumed manually.

Timeline and crash evidence

For the latest occurrence on July 15 (local time, UTC-03:00):

intermediate AppModel activation PID 24152:       09:54:23.415
previous root ChatGPT.exe PID 17140 last log: 09:56:56.715
new root ChatGPT.exe PID 35372 created:          09:57:04.755
gap from the previous root's last log to the new root: approximately 8 seconds

There was therefore an intermediate package activation before the previous root stopped, rather than a simple one-process transition. The old log ended abruptly without a normal shutdown sequence. However, I found no corresponding:

  • Windows Application Error event 1000;
  • Windows Application Hang event 1002;
  • WER report or CrashDump;
  • new Crashpad dump.

The new process entered the same packaged-app container. This looks like abrupt process termination followed by a quick relaunch, rather than a documented native crash.

Error burst immediately before termination

During the 09:56:00-09:56:56.715 portion of the old process log there were 171 lines, including:

107 error-level records
73 events for an unknown conversation
30 "Conversation state not found" records
4 JSON/NUL parse errors

From 09:52:00 until termination there were 320 error-level records, including 227 unknown-conversation records, 87 missing-conversation-state records, and 22 JSON/NUL parse errors. These categories overlap, so their subtotals are not additive. I cannot prove that this error burst caused the UI hang, but it coincided with this occurrence and the log ended immediately afterward. The NUL parse failures themselves have recurred across multiple runs.

Persistent all-NUL chat_processes.json

I found a concrete persistent corruption that was not included in my original report:

file: %USERPROFILE%\.codex\process_manager\chat_processes.json
size: 13,445 bytes
content: every byte is 0x00
last modified: 2026-06-06 00:31:03 local time
SHA-256: 708E86177F15E191D0622C42434FCC7E09C401D00BC003E2B3242DD02EEE59DB

The Desktop app has continued attempting to parse this same invalid file across upgrades and repeatedly logs JSON parse failures. Inspection of the currently packaged JavaScript shows that the reader treats ENOENT as an empty process-record list. With invalid JSON, parsing fails and the higher-level handler repeatedly logs the failure. The writer recreates the directory/file with a JSON array when it next persists records.

This file is a process-association cache, not the durable conversation store. A conservative local repair is therefore to close the app completely and move the all-NUL file to a dated backup, leaving it absent so the implemented ENOENT fallback yields an empty list; the normal writer can then recreate it. I am preserving the original file for diagnostics.

Log database growth and integrity checks

Shortly after the recurrence:

logs_2.sqlite:     985.24 MiB
logs_2.sqlite-wal:  45.72 MiB
main DB growth: approximately 91.6 MiB since the previous day

PRAGMA quick_check(1) returned ok for both logs_2.sqlite and state_5.sqlite. The log database contains diagnostic logs separately from the conversation state database.

The important recovery result is that this occurrence did not delete the interrupted conversations:

  • state_5.sqlite passed quick_check;
  • .codex-global-state.json and its backup both parsed successfully;
  • session_index.jsonl parsed successfully;
  • the rollout files for the interrupted tasks still existed and ended in valid JSON.

The tasks were interrupted, but their persisted history remained recoverable. The app did not automatically continue the running turns after relaunch.

One workspace volume was also critically low on free space at the time, so I cannot rule out additional I/O pressure as a contributor to the hang. That does not explain the long-lived all-NUL process cache, and forced termination still needs a deterministic recovery path for active tasks and local state.

What this adds to the original report

The failure mode is broader than physical power loss:

  1. the UI can hang during ordinary use;
  2. the Windows default End Process flow can turn that hang into an ungraceful termination;
  3. an internal process-manager JSON file can remain entirely NUL-filled across multiple app upgrades;
  4. repeated parse/state-routing errors and a rapidly growing log database accumulate before termination;
  5. durable conversation data survives, but running work is not automatically recovered.

This reinforces the need for atomic writes plus startup validation/quarantine of invalid local state files. A startup check could detect an all-NUL process_manager/chat_processes.json, preserve it as a diagnostic artifact, rebuild the process cache, and reconcile still-running/interrupted turns from durable conversation state instead of repeatedly parsing the same invalid file.

kinglbworld · 1 month ago

July 27 recurrence on Windows app 26.721.4979.0: repaired pins are overwritten by stale in-memory state

I can reproduce the same crash-safety failure on a substantially newer Windows Store build.

Environment

  • Codex package: OpenAI.Codex_26.721.4979.0_x64__2p2nqsd0c76g0
  • Platform: Windows 11 x64
  • Occurrence: 2026-07-27, after the desktop app crashed and was relaunched

Observed behavior

  • The profile should contain 6 pinned local threads.
  • Immediately after the crash/relaunch, the sidebar showed exactly 3 pins.
  • Both %USERPROFILE%\.codex\.codex-global-state.json and .codex-global-state.json.bak parsed successfully, but both contained the same reduced 3-entry pinned-thread-ids array.
  • The missing threads were still present in state_5.sqlite, session_index.jsonl, and the rollout files. Their content was not deleted; this is a projection/persistence loss.
  • Project membership is affected too: existing thread-to-project mappings can be recovered, while a newly created thread whose cwd was the saved project root was returned with no project id and therefore risked disappearing from that project's sidebar.

Stronger deterministic evidence

I backed up the files before every repair and performed the following sequence:

  1. Atomically replace both the main JSON and its .bak with valid JSON containing all 6 known pinned thread ids.
  2. Read both files back and verify that both contain 6 entries.
  3. The repaired disk state remains unchanged while the app is idle. In one run it stayed at 6 for a monitored 15-second interval.
  4. Re-pin/hydrate the 3 missing threads: the live task list temporarily reports all 6 pins.
  5. On the next ordinary task-list/sidebar refresh, the app rewrites both the main file and .bak back to the old 3-entry set. In the captured run, both files were rewritten at approximately 23:21:39 local time.
  6. Repeating the sequence gives the same result: the externally verified 6-entry state is replaced by the stale 3-entry state held by the running app.

This looks like a lost-update/stale-renderer-state bug, not just a torn JSON write. The running renderer/store keeps an older subset, then later persists the entire stale snapshot and overwrites a richer valid generation. The .bak file is not an independent recovery source because it is overwritten with the same stale subset in the same update.

Expected behavior / suggested invariants

  • Pin/unpin and project-assignment mutations should have a single persistence owner or generation/compare-and-swap check.
  • A stale renderer should not replace a newer valid state generation.
  • State updates should merge the changed key instead of serializing an old full snapshot over unrelated newer data.
  • Large unexplained shrinkage in pins, projects, project assignments, or workspace hints should trigger reconciliation from the durable thread catalog before commit.
  • The backup/checkpoint should not be overwritten with the same regressed generation as the main file.
  • A supported codex doctor --reconcile-state-style recovery path would avoid manual JSON repair.

I am not attaching raw databases, logs, full local paths, project names, or thread ids publicly because they may contain private conversation metadata. I can provide redacted field-level diagnostics if a maintainer specifies what is needed.

kinglbworld · 1 month ago

July 28 recurrence: live project hydration is wrong even when persisted state is healthy

This happened again on the same Windows 11 machine and the same package build:

OpenAI.Codex_26.721.4979.0_x64__2p2nqsd0c76g0

The new evidence narrows the failure from persistence corruption to a startup/live-projection problem as well.

Persisted state was already healthy

Immediately after the user reported that pinned/project conversations had disappeared again, I audited the local state before changing it:

  • Both .codex-global-state.json and .codex-global-state.json.bak parsed successfully.
  • Both already contained all 6 known pinned thread ids.
  • The main state contained 93 thread-project-assignments entries.
  • A deterministic cwd-to-saved-project audit found 92 non-archived user threads mapped across 16 saved local projects.
  • Re-running the repair/reconciliation pass reported 0 corrected project assignments and preserved all 6 pins.
  • The audit explicitly excluded 187 internal subagent/guardian rows so that recovery would not surface internal derived threads as duplicate user conversations.

So, unlike the previous recurrence, the persisted JSON did not need repair.

Live task projection was still wrong

Despite the healthy persisted state, the initial live task-list response returned all 6 pinned threads but reported projectId: null for 3 pinned threads whose on-disk assignments correctly pointed to the same saved local project. The sidebar consequently treated these conversations as missing or outside their project.

I then re-issued an idempotent pin mutation (pinned=true) for those same 3 thread ids. Immediately afterward, the live task list still contained the same 6 pins, but the 3 affected rows now had the correct project id. No disk-level project assignment had changed; the mutation only forced the live model to rehydrate/reconcile the rows.

What this suggests

This is not only a torn-write or stale-full-snapshot problem. There also appears to be a startup/live hydration mismatch:

  • the durable global state can contain the correct thread-to-project assignment;
  • the live sidebar/task projection can still expose the same thread with projectId: null;
  • a logically unrelated idempotent pin mutation causes the correct project assignment to appear.

Suggested invariant/test:

  1. On startup, load and validate thread-project-assignments before publishing sidebar/task rows.
  2. If a live row has projectId: null but durable state has a valid assignment and the thread cwd matches that saved project, reconcile instead of dropping the relationship.
  3. Pin/unpin hydration must not be the operation that incidentally repairs project membership.
  4. Add a restart test where the JSON is fully valid and complete, but the in-memory/task projection begins empty or stale; verify all pins and project memberships are visible without a mutation.
  5. Keep internal subagent/guardian threads excluded from user recovery to avoid duplicate sidebar entries.

This recurrence further supports adding a supported codex doctor --reconcile-state path and startup projection validation. I can provide additional redacted counts or field-level diagnostics if maintainers request them.

kinglbworld · 24 days ago

August 4 recurrence after Windows app update: live pins are empty despite healthy persisted state

This issue has recurred again after updating and restarting the Windows desktop app.

Environment

  • Codex package: OpenAI.Codex_26.727.6591.0_x64__2p2nqsd0c76g0
  • Platform: Windows 11 x64
  • Occurrence: 2026-08-04, immediately after an app update/restart and again after a crash/relaunch

Deterministic pin evidence

The expected set is 6 pinned local threads.

  1. Both %USERPROFILE%\.codex\.codex-global-state.json and its .bak parse successfully.
  2. Both files already contain the same complete 6-entry pinned-thread-ids array.
  3. The live task-list API nevertheless reports pinCount: 0 and no pinned rows.
  4. I issued pinned=true for each of the same 6 known thread ids. Every mutation returned success.
  5. A fresh live list immediately afterward still reports pinCount: 0.
  6. Re-running the disk reconciliation pass makes no pin changes because the persisted state is already correct.

This is stronger evidence of a live hydration/projection failure: a successful mutation is acknowledged, but the live store remains empty even while both durable JSON generations contain the complete set.

Project/session audit

A redacted local audit found:

  • state_5.sqlite: 395 thread rows, 394 active, 1 archived; PRAGMA quick_check succeeds.
  • Persisted thread-project-assignments: 110 entries across 17 saved local projects.
  • One heavily used project still has all 31 expected active user threads, so the failure is not a uniform database wipe.
  • 39 older assignment ids no longer have corresponding rows in either state_5.sqlite or session_index.jsonl.
  • 38 of those 39 ids are recorded in external_agent_session_imports.json as previously imported external-agent sessions.
  • For 15 of those imported records, the original source JSONL still exists locally, so the desktop app could re-import/reconcile them; the remaining source files are no longer present.
  • Recent live task rows also expose no project id even though durable project assignments exist.

No raw databases, local paths, project names, titles, or thread ids are being posted publicly.

Why this matters

The app now has at least three mutually inconsistent views of the same state:

  • durable global JSON: all 6 pins and 110 project assignments;
  • SQLite/session index: 395 current rows but 39 historical assigned ids missing;
  • live sidebar/task projection: 0 pins and missing project ids.

The current update/restart path therefore does not merely lose a write. It can fail to hydrate the live model from valid durable state, acknowledge idempotent mutations without reflecting them, and leave the external-import ledger claiming sessions were imported after their catalog rows disappeared.

Requested fix

Please add a startup reconciliation invariant/test for this exact case:

  • If durable JSON has valid pinned ids, the published live task list must contain the same pins before rendering the sidebar.
  • A successful set pinned=true operation must be observable in a subsequent list call.
  • Reconcile external_agent_session_imports.json against state_5.sqlite and session_index.jsonl; if an import record exists but its thread row is missing and the source file still exists, re-import it automatically.
  • Do not publish projectId: null when a valid durable assignment exists and cwd matches the saved project.
  • Add generation/CAS protection so an empty or stale live snapshot cannot overwrite a richer valid persisted generation.
  • Provide a supported codex doctor --reconcile-state command that repairs pins, project assignments, session indexes, and stale import-ledger records without manual SQLite/JSON surgery.

This has now happened repeatedly across multiple Windows desktop builds and is triggered by ordinary app updates/restarts as well as crashes. It is causing persistent, user-visible disappearance of pinned and project conversations.

LYUml · 23 days ago

August 4 Windows reproduction on 26.727.6591.0: healthy local history remains hidden until threads are pinned

I encountered a project-sidebar history disappearance on the current Windows Store build:

OpenAI.Codex_26.727.6591.0_x64__2p2nqsd0c76g0
Windows 11 x64
Codex CLI 0.146.0

This report is sanitized. Project names, local paths, username, prompts, and thread IDs are omitted.

User-visible behavior

  • Five saved local projects remained visible.
  • Four projects with historical conversations displayed No chats.
  • Thirteen real user conversations were hidden.
  • Restarting and reloading Codex Desktop did not restore them.

Evidence that the data was intact

A Codex assistant inspected the local state and confirmed that:

  • The rollout JSONL files still existed.
  • state_5.sqlite contained the matching active rows with valid rollout paths.
  • .codex-global-state.json retained the saved projects and thread-to-project assignments.
  • Direct thread reads and resumes succeeded.
  • codex doctor reported healthy state databases and agreement between the rollout files and database inventory.
  • No local host or conversation source was unavailable.

This established that the conversations had not been deleted.

Recovery investigation performed by Codex

After searching the public openai/codex GitHub issues, the Codex assistant identified #25500, #25463, and #26990 as closely matching reports.

With the user’s approval, Codex then tested the recovery methods suggested by those reports:

  1. Reconciled stale thread-to-project metadata.
  2. Restored one archived affected thread.
  3. Restarted Codex Desktop.
  4. Resumed all 13 real user threads and appended one minimal OK turn.
  5. Verified that the live thread-list path could enumerate all 13 threads under the expected project paths.

Codex also identified a compatibility difference: older affected rows used model_provider = openai, while newly created Desktop rows used model_provider = chatgpt-http. Updating this metadata made the live thread-list path recognize the older rows.

However, none of these actions restored the conversations in the Projects sidebar. The projects continued to show No chats.

Codex then tested the pinning workaround reported on GitHub by setting pinned=true for the same 13 thread IDs.

All 13 conversations immediately became visible in Codex Desktop.

What this narrows down

The investigation indicates a Desktop live-hydration or sidebar-projection failure rather than data loss:

  • durable rollout and SQLite state was healthy;
  • direct reads and resumes worked;
  • the live thread list could enumerate the conversations;
  • adding real new activity was insufficient;
  • pinning was the event that caused the Desktop UI to hydrate and display them.

Suggested regression test

  1. Seed multiple non-archived user threads under saved project paths.
  2. Keep the threads outside the initial recent-thread window.
  3. Restart Desktop and publish the initial sidebar state.
  4. Assert that expanding each project performs project-scoped pagination or fallback instead of displaying No chats.
  5. Assert that project membership and visibility are identical before and after pinned=true.
  6. Cover legacy model_provider = openai rows alongside current chatgpt-http rows.
  7. Cover both normal and Windows verbatim path forms.

Pinning is currently an effective workaround on this machine, but users should not need to pin every historical conversation to access valid project history.

kinglbworld · 18 days ago

August 10 recurrence on 26.803.5235.0: ordinary reopen republishes stale sidebar state after successful disk repair

This is another recurrence on the current Windows desktop package:

OpenAI.Codex_26.803.5235.0_x64__2p2nqsd0c76g0
Windows 11 x64
Occurrence: 2026-08-10
Trigger: ordinary app close/reopen; no power loss required

I am reporting this as a serious reliability issue because it has now reproduced across multiple Windows builds, including 26.727.6591.0, 26.730.7989.0, 26.730.8199.0, and 26.803.5235.0.

Deterministic sequence observed

  1. The expected state contains 6 pinned local threads.
  2. A post-exit reconciliation ran successfully before the next launch.
  3. That reconciliation logged:
  • persisted pins before repair: 3
  • persisted pins after repair: 6
  • 10 superseded/stale project-assignment ids removed
  • 105 valid active project assignments reconstructed
  1. The app was then reopened normally.
  2. The sidebar again omitted pinned/project conversations even though durable state and SQLite were healthy.
  3. I issued an explicit unpin→pin sequence for the same 6 known threads through the live app API.
  4. All 6 mutations returned success and the sidebar was forced to hydrate them again.

Current redacted local evidence

After live recovery:

.codex-global-state.json pinned ids: 6
.codex-global-state.json.bak pinned ids: 6
state_5.sqlite pinned active rows: 6
state_5.sqlite total rows: 472
state_5.sqlite active rows: 471
PRAGMA quick_check: ok
valid active project assignments: 105
new missing active project conversations: 0

Several heavily used projects retain stable active counts, including 33, 14, 14, 10, and 6 threads respectively. This rules out a uniform database wipe during this recurrence.

Stale in-memory overwrite evidence

On the immediately preceding recurrence with the same build family, the running desktop process rewrote a repaired JSON generation from 6 pins back to 3 and reinserted 10 assignment ids that had already been superseded by successful reimports. SQLite continued to report all 6 pins.

After the process exited, the repair pass again observed beforePinCount: 3, removed the same 10 stale ids, and restored afterPinCount: 6.

This strongly suggests a stale in-memory snapshot or renderer/main-process projection is being persisted over a richer, valid generation. The problem is not limited to crash-unsafe writes: ordinary launch/reopen can publish or persist stale sidebar state after a successful repair.

Severity and impact

  • Users perceive pinned and project conversations as deleted.
  • Repeated restarts do not reliably fix the sidebar and can reintroduce stale ids.
  • Durable JSON, SQLite, import ledger, and live sidebar can disagree.
  • Recovery currently requires private thread ids plus manual JSON/SQLite reconciliation or live pin mutations.
  • This has recurred repeatedly for weeks across several shipped Windows builds.

Requested engineering changes

  1. Add a generation/CAS guard so stale in-memory state cannot overwrite a richer persisted generation.
  2. On startup, reconcile pinned ids and project assignments before publishing the sidebar.
  3. Assert that a successful pin mutation is observable in the next live list and in the rendered sidebar.
  4. Reconcile imported-session ledger entries with SQLite and rollout existence without creating duplicates.
  5. Add a supported codex doctor --reconcile-state operation for pins, project membership, stale assignments, and session indexes.
  6. Add a Windows regression test covering ordinary update/reopen, not only crashes or power loss.

No private prompts, thread ids, project names, usernames, or raw databases are included here. I can provide further redacted field-level diagnostics if a maintainer requests them.

n5-rm-dark · 15 days ago

I hit what appears to be the same failure mode after repeated abrupt Windows shutdowns, and I was able to fully recover the local projects including the original project/thread grouping without modifying the conversation contents.

What was actually lost

The conversations themselves were still intact.

In my case:

  • state_5.sqlite still contained 313 threads
  • every thread still had a cwd
  • the project folders on disk were intact
  • session/chat contents were intact

What had been reset was the project/index state in:

%USERPROFILE%\.codex\.codex-global-state.json

Immediately before recovery, the state had effectively collapsed to:

  • local projects: 1
  • project-assigned threads: 1

After reconstruction:

  • local projects: 14
  • 1 existing workspace
  • 13 project folders
  • project-assigned threads: 265
  • active projectless threads: 42
  • unassigned archived threads: 6
  • conversation contents modified: 0

Important schema difference

In this current Windows build, I did not find:

thread-workspace-root-hints

before recovery, immediately after recovery, or after restarting Codex.

The project/thread mapping used by this installation was instead stored primarily in:

  • local-projects
  • project-order
  • thread-project-assignments
  • thread-writable-roots
  • projectless-thread-ids

So recovery logic should not assume that thread-workspace-root-hints exists across app versions.

Recovery approach

With Codex fully closed:

  1. Backed up .codex-global-state.json and its .bak.
  2. Created a consistent snapshot of state_5.sqlite using SQLite's backup API.
  3. Queried:
SELECT id, cwd, archived
FROM threads
ORDER BY created_at;
  1. Enumerated existing project directories containing PROJECT_CONTEXT.md.
  2. Normalized Windows paths, including stripping a leading \\?\ where present.
  3. Matched each thread's cwd to:
  • an exact project root,
  • a descendant of a project root,
  • known legacy/moved project paths where applicable.
  1. Rebuilt only these top-level state keys:
  • local-projects
  • project-order
  • thread-project-assignments
  • thread-writable-roots
  • projectless-thread-ids
  1. Did not modify state_5.sqlite, session files, chat titles, chat contents, or archive status.
  2. Wrote the new JSON to a temporary file, flushed it with fsync, then atomically replaced the original with os.replace().
  3. Restarted Codex and verified project lists, representative threads, archives, and search.

For local project IDs, I generated deterministic UUIDv5 IDs from the normalized absolute project path, while preserving the ID of the existing workspace.

Result

The full local project organization was restored successfully.

This suggests that, at least in some current Windows builds, a crash/power-loss recovery feature could potentially reconstruct missing project metadata automatically from:

  • state_5.sqlite thread IDs / cwd
  • existing project directories
  • existing session data

rather than treating missing project state as deleted conversations.

I would strongly prefer an official repair/reindex command rather than editing internal state manually.

Safety note

I am not publishing my raw .codex-global-state.json, SQLite databases, sessions, logs, or the exact recovery script because they contain local paths/project information and may contain private user data.

The recovery method above depends on undocumented internal state and may be version-specific. It should be considered an emergency recovery procedure, not a supported general-purpose fix.

If useful to maintainers, I can produce a sanitized recovery script with configurable --codex-home, --workspace-root, --projects-root, --aliases-file, --dry-run, and --output-report parameters.

safal207 · 15 days ago

Following up with an executable artifact rather than another design sketch.

The failure classes reported in this thread are now encoded as a small vendor-neutral conformance fixture in LS:

The fixture keeps these surfaces separate:

durable conversation/thread state
!= persisted project/index projection
!= live sidebar/task projection

It currently covers eight deterministic vectors:

  1. collapsed projection while durable threads remain intact;
  2. stale generation attempting to overwrite a richer committed projection;
  3. healthy persisted assignments with broken live hydration;
  4. schema variation with no thread-workspace-root-hints;
  5. Windows normal vs \\?\ verbatim path equivalence;
  6. ambiguous project-root matching failing closed;
  7. projection repair attempting to mutate conversation content;
  8. idempotent second recovery.

The important adjustment from the newer reproductions here is that recovery does capability/schema discovery first; it does not assume one global-state key set across builds.

The core conformance invariant is:

repair derived projection
must not mutate conversation contents

recovery(recovery(state)) == recovery(state)

The dedicated fixture CI is passing, including generation blocking, path normalization, content-preservation and a redaction-safe reconciliation report.

This is not a Codex repair script and does not touch real user state; the goal is to provide a portable regression oracle for the failure mode described in this issue. If a native Codex recovery/reindex implementation lands, I’d be happy to adapt the vectors to its preferred interfaces.

kinglbworld · 14 days ago

Severe recurrence on Windows — 2026-08-14

This serious persistence bug has reproduced again on OpenAI.Codex_26.803.10989.0_x64__2p2nqsd0c76g0.

After an ordinary desktop update/reopen—and again after a client crash—pinned conversations disappeared and conversations from multiple unrelated projects no longer appeared under their projects. The database integrity check itself passed, so this is consistent with a migration/reconciliation/persistence failure rather than ordinary database corruption.

A local recovery was able to restore missing imported conversations, rebuild project membership from live thread metadata, and restore the required pinned set. However, the same failure has repeatedly returned after later updates or restarts.

This pattern has now been observed across at least these Windows desktop builds:

  • 26.727.6591
  • 26.730.7989
  • 26.730.8199
  • 26.803.5235
  • 26.803.10989

Please treat this as a high-severity persistent-state/data-loss issue. The desktop app needs:

  • transactional migration of the thread database, rollout store, project assignments, and pinned IDs;
  • startup reconciliation before sidebar state is trusted;
  • import-ledger reconciliation for missing imported sessions;
  • generation/CAS protection against stale shutdown or migration writes;
  • crash-safe atomic persistence for pinned and project-assignment state;
  • an official recovery command such as codex doctor --reconcile-state;
  • regression tests covering update, crash, and reopen flows with many conversations across multiple projects.

A user-space watcher is not a durable workaround because it can be terminated by the same update, crash, or reboot that triggers the loss. The repair needs to be native and run automatically on every startup.

safal207 · 14 days ago

This recurrence adds one important discriminator to the failure model: abrupt power loss is no longer a required trigger. An ordinary update/reopen can produce the same loss of pins/project projection while the canonical thread database remains internally valid.

That suggests the regression contract should explicitly separate canonical conversation state from derived UI/project projection:

canonical threads / rollout evidence
        ↓
startup migration + reconciliation
        ↓
project membership / imported-session projection
        ↓
pins + sidebar visibility

A useful invariant for a native fix/test would be:

If canonical thread/session evidence for generation N still exists,
startup/update/recovery must not commit a projection for N+1 that
silently drops previously valid project membership or required pins,
unless an explicit user mutation authorizes that removal.

The LS conformance fixture I linked earlier can model this recurrence as a distinct vector rather than treating it as another power-loss case:

ordinary_update_reopen_preserves_projection

with pre/post assertions over at least:

thread_count
project_count
project_assigned_thread_count
imported_thread_count
required_pin_count
state_generation
reconciliation_result

The key property is monotonic preservation of recoverable membership across startup reconciliation, plus generation/CAS rejection of any stale or incomplete writer. That seems to match the new evidence more closely than a generic SQLite-corruption test.

safal207 · 14 days ago

Follow-up with the executable vector for the new trigger class rather than another design note.

The 2026-08-14 recurrence showed that abrupt power loss is not required: an ordinary desktop update/reopen can be enough to lose project membership and pinned conversations while durable thread evidence remains intact.

I encoded that transition as a new vendor-neutral LS conformance vector:

The transition model is deliberately stricter than the earlier recovery-only fixture:

trusted committed projection
        +
authoritative durable threads
        ↓
update / reopen / startup reconciliation
        ↓
candidate projection
        ├─ stale generation → BLOCK
        ├─ content mutation → BLOCK
        ├─ silent project-membership loss → BLOCK
        └─ silent pinned-state loss → BLOCK

The frozen recurrence vector gives the candidate a newer generation but removes two still-supported project memberships and two pins. With no explicit user mutation authorizing that reduction, the oracle returns BLOCK_REGRESSIVE_PROJECTION and keeps the trusted generation authoritative.

I also added negative controls: a complete newer projection is accepted, an explicit user-authorized reduction is accepted, and a stale startup writer is blocked.

One important distinction in the fixture: project membership may be reconstructable from durable thread metadata, but pins are user-authored state. The oracle therefore preserves pins rather than attempting to synthesize them from cwd/path evidence.

All deterministic tests, both redacted reports, and artifact upload are green in the linked run. As before, this is synthetic conformance data and does not read or modify real Codex state.

safal207 · 14 days ago

Following up with the merged executable artifact for the 2026-08-14 update/reopen recurrence.

The transition-level conformance vector is now merged in safal207/LS#932 at squash commit fa7e3aba4ff9154856fa7d27c92f702137819ac1.

It now exercises the startup/reconciliation boundary directly:

  • an ordinary update/reopen candidate that silently drops still-supported project membership or pinned state is rejected;
  • stale generations are rejected;
  • missing, string-valued, or boolean generations fail closed rather than bypassing the generation guard;
  • an explicit user mutation is a control case and may intentionally reduce projection state;
  • conversation-content mutation is rejected independently of projection correctness;
  • pins are treated as user-authored state to preserve, not something reconstructed from cwd/path evidence.

The CI/logging boundary was also hardened after security review: detailed synthetic case reports stay in-process, while the published transition artifact contains only aggregate counts over fixed verdict classes.

Final validation on the merged head included the dedicated state-projection conformance workflow plus the repository security pipeline: hardening tests, quality gate, vulnerability audit, SBOM generation, dependency review and CodeQL all passed. GitHub Advanced Security reports no new alerts in code changed by PR #932.

This remains a vendor-neutral regression oracle, not a Codex repair script. If a native startup reconciler or a supported recovery command such as codex doctor --reconcile-state lands, these vectors can be adapted to that interface.

atomicdjt · 14 days ago

One architectural direction that may reduce the number of independent recovery paths here: treat the JSON/sidebar state as a rebuildable projection, not a second authority.

The failure pattern is especially hard to recover when several stores can each look individually valid but describe different generations of state. A small generation/commit marker across the durable stores would make that detectable.

For example:

SQLite / append log = authoritative records
        ↓
projection generation N
        ↓
.codex-global-state.json (atomic temp + fsync + rename)
session index / sidebar cache (same generation N)

On startup:

  1. validate JSON checksum/schema and generation;
  2. compare its generation with the authoritative store;
  3. if missing, older, or impossible (including implausible timestamps), rebuild the projection instead of accepting a minimal/default replacement;
  4. preserve the broken projection for diagnostics rather than overwriting the only evidence;
  5. expose the same logic as a dry-run repair command so recovery is inspectable before mutation.

I would also distinguish missing, stale, and corrupt rather than mapping all three to “initialize defaults.” A stale-but-readable file is exactly the dangerous case here: it can silently look legitimate while dropping projects/pins.

That gives the UI a useful invariant: if the authoritative thread records still exist, losing a cache/projection may affect startup time, but should not affect whether a project appears to exist. The projection becomes disposable and recoverable rather than load-bearing user data.

shleder · 14 days ago

I’m testing Codex Rescue 0.1.0a3 against crash/power-loss recovery cases. Your report is useful because the global/UI/SQLite state drifted badly after abrupt shutdown, while many underlying rollout files remained recoverable.

Rescue works from local rollout data, does not modify Codex SQLite/global state, and fails closed when it cannot prove a safe continuation. It does not repair .codex-global-state.json, timestamps, or config migration.

If you still have one affected pre-repair session, would you be willing to try:

pipx install codex-rescue==0.1.0a3
codex-rescue sessions
codex-rescue doctor --latest

If it identifies the right rollout, sanitized salvage --fork / verify output would help validate the rollout-level recovery boundary. Please don’t post raw rollouts, SQLite DBs, config/auth files, prompts, credentials, or private paths.

https://github.com/shleder/codex-rescue