Codex App hides existing threads when a project is reopened via a symlinked workspace path on macOS
What version of the Codex App are you using (From “About Codex” dialog)?
26.318.11754 (1100)
What subscription do you have?
Plus
What platform is your computer?
macOS, Apple Silicon (M3)
What issue are you seeing?
After moving some projects to an external SSD and restoring them through symlinks, Codex App stopped showing the old threads for those projects in the sidebar/history UI.
The thread data was not actually lost. The local Codex data still existed under ~/.codex, including the thread database and session index. The issue only affected projects that had been moved to the external drive.
From local inspection, this appears to be a workspace path canonicalization mismatch:
- the desktop app persisted workspace roots using the symlink path, such as
/Users/x3zvawq/workspace/... - existing thread metadata stored
cwdusing the real path, such as/Volumes/SSD/workspace/...
Because of that mismatch, the app seems to treat them as different projects and does not show the old threads for the symlink-opened workspace, even though both paths resolve to the same location.
What steps can reproduce the bug?
- Have an existing project with Codex App threads/history already created.
- Move that project to an external SSD.
- Create a symlink from the old path to the new location, for example:
- old logical path:
/Users/x3zvawq/workspace/my-project - real path on external drive:
/Volumes/SSD/workspace/my-project
- Open the project in Codex App using the symlinked path.
- Observe that older threads for that project no longer appear in the app.
- Inspect local state and thread metadata:
- app state may still contain workspace roots under the symlink path
- thread records may store
cwdunder the real path
- Rewriting the saved workspace-root paths in
~/.codex/.codex-global-state.jsonfrom the symlink path to the real path makes the missing threads show up again after restarting Codex App.
What is the expected behavior?
Codex App should treat a symlinked workspace path and its resolved real path as the same workspace when grouping, matching, or displaying project threads.
In other words, if:
- saved workspace root =
/Users/x3zvawq/workspace/my-project - thread cwd =
/Volumes/SSD/workspace/my-project
and both resolve to the same filesystem location, the existing threads should still appear normally in the app.
Additional information
I investigated the local state and found that the thread data itself was intact:
~/.codex/state_5.sqlite~/.codex/logs_1.sqlite~/.codex/session_index.jsonl
A manual workaround was:
- close Codex App
- back up
~/.codex/.codex-global-state.json - rewrite saved workspace-related paths from the symlink form to the real resolved path
- reopen Codex App
After doing that, the missing threads reappeared.
This suggests the bug is likely in desktop-side workspace path persistence/comparison rather than data loss.
Possible fix direction:
- canonicalize or resolve
realpathfor workspace roots before persisting and before comparing them against threadcwd - alternatively, compare both sides using a normalized/canonical filesystem path representation instead of raw path strings
- add a regression test covering symlink path vs resolved path for the same workspace
9 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
This looks closely related to, but not identical with:
My case is on macOS and appears to involve a symlink path vs resolved real path mismatch:
/Users/x3zvawq/workspace/.../Volumes/SSD/workspace/...The thread data still exists locally, and rewriting the saved workspace path to the resolved real path makes the threads reappear.
So this seems closest to #13846 at the root-cause level (path normalization / canonicalization mismatch), while #15347 is a broader product feature request rather than the same bug.
After applying the initial workaround and restarting the app, all missing threads became visible again in the desktop UI.
However, a second issue appeared:
From local inspection, this did not look like actual data loss:
~/.codex/state_5.sqliteSo this seemed to be a second desktop-side UI/filtering issue triggered after opening a restored thread, rather than the thread actually being deleted or lost.
Will this be fixed? Why are threads disappear?
Same issue. It's the second time I update, recent-ish threads are kept, 2-week-ish ones are gone. Is there some type of default filtering going on by date? Or perhaps a reset per install?
Comment drafted by Claude (Anthropic) and posted by @acpatel after empirical test on a personal macOS workspace; technical claims verified against
openai/codexsource.Root-cause confirmation across the path-divergence reports.
Comparing #15448 with #11019 / #12679 / #13846 — they reduce to the same shape:
session_meta.cwdis written once at thread creation in the rollout JSONL and never updated, so any later divergence between the workspace path Codex Desktop is opened in and that recordedcwddrops the thread out of the chat-list filter. Triggers seen across the four issues: Finder folder move, symlink ↔ realpath canonicalization, Windows UNC ↔ drive-letter, andLibrary/CloudStorage/OneDrive-X/ABC↔ relocated/Users/X/Y/ABC. The thread data is intact in~/.codex/sessions/; only the listing hides it.From
ThreadResumeParams.ts: resume is bythread_idwithcwdaccepted as an override — so the recordedcwdisn't load-bearing for resume itself. The failure is purely in the chat-list filter, which never surfaces the thread to be resumable.One-shot user workaround (tested on macOS 15, on a 6-week-old thread, 39MB / 13,980 records / 29 compactions — thread reappeared and resumed cleanly):
Body content (
function_call_outputpayloads etc.) intentionally left alone — those are historical tool-call transcripts, often containing the path the tool ran in at the time, and rewriting them either falsifies that record or breaks Python adjacent-string-literal source captured in pastbashoutputs.Proper fix candidates (rough ascending complexity):
cwdon both sides before the chat-list filter compares (std::fs::canonicalizeor equivalent). Resolves #15448 directly.cwd(per #12679's acceptance criteria). Recovers from arbitrary moves like #11019.session_meta.cwddoesn't resolve to the current workspace.I am seeing the same failure mode on macOS with Codex CLI
0.136.0.The important invariant break is that saved project roots and thread
cwdrows are not normalized the same way.In
~/.codex/.codex-global-state.json, Codex has saved project roots such as:Those paths are symlinks:
But
~/.codex/state_5.sqlitestores active thread rows under a mix of the symlink path and the resolved realpath:| Saved project root | Realpath | Active threads at saved root | Active threads at realpath |
|---|---|---:|---:|
|
<home>/Desktop/SignalBrief|<home>/repos/signalbrief/SignalBrief| 0 | 26 ||
<home>/Desktop/AI Trading Signals|<home>/repos/personal/ai-trading-signals| 67 | 6 |For SignalBrief, the project is saved under the symlink path, but all active threads are under the realpath, so the project chat list can show no chats even though the threads still exist locally and are not archived.
codex doctor --summary --no-color --asciireports:This looks like project identity is being compared by exact raw path string in at least one UI/thread grouping layer, while other layers resolve the path to realpath.
Expected behavior: Codex should either canonicalize saved project roots and thread
cwdconsistently, or preserve the user-selected symlink identity consistently. If both symlink and realpath are present, the UI should merge/warn/remap instead of making chats appear lost.So, is there any way—even a temporary fix—to address this issue? The current state is completely unusable.
I hit a closely related variant in Codex Desktop on macOS today.
Context:
~/Desktop/OldProjectName.~/Desktop/NewProjectName.cwdpointing to the old logical path.Observed behavior:
~/Desktop/OldProjectNameto~/Desktop/NewProjectName, Codex Desktop showed the selected project as missing with the banner equivalent to "This project folder has been deleted or moved".What I verified locally:
list_projectsstill reported the project label as the new project name, but the saved project path was the old path.list_threadsshowed many historical threads withcwdequal to the old path.pwd -Pfrom the old symlink path resolved to the new real folder, and git status/log/remote were all correct.Expected behavior:
Workaround:
Why this matters: