codex - 💡(How to fix) Fix Codex Desktop project rename updates label but leaves workspace root and cwd metadata stale [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#22075Fetched 2026-05-11 03:19:32
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Timeline (top)
labeled ×3commented ×1

Codex Desktop can put a project into a split state after using the project rename UI:

  • the sidebar display label changes
  • the underlying workspace root remains ~/Documents/New project N
  • the actual folder on disk remains ~/Documents/New project N
  • existing thread rows and rollout metadata continue to reference the old root

The visible result is a sidebar row that shows both the renamed project label and the old autogenerated folder name, for example:

Vera Companio...    New proj...

This was reproducible across multiple renamed projects in the same profile, not just one project.

Root Cause

I am filing this in openai/codex because it is the public repo linked to Codex installs and it contains the app-server, protocol, thread-store, rollout, and state-db code that participate in this bug. However, the Desktop/Electron code that appears to own the project sidebar and global Desktop state does not appear to be present in this public repository.

Fix Action

Fix / Workaround

The request processor also requires gitInfo and builds a Git-only patch:

let ThreadMetadataUpdateParams { thread_id, git_info } = params;
...
let Some(ThreadMetadataGitInfoUpdateParams { sha, branch, origin_url }) = git_info else {
    return Err(invalid_request("gitInfo must include at least one field"));
};
...
let patch = StoreThreadMetadataPatch {
    git_info: Some(StoreGitInfoPatch { ... }),
    ..Default::default()
};

Code Example

Vera Companio...    New proj...

---

Codex Desktop: 26.506.31421
Bundle id: com.openai.codex
Platform: macOS arm64

---

2abdeb3 Read cached metadata for installed Git plugins (#20825)

---

<strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.
...
If you want the desktop app experience, run <code>codex app</code> or visit ...

---

{
  "name": "openai-codex-electron",
  "productName": "Codex",
  "version": "26.506.31421",
  "main": ".vite/build/bootstrap.js",
  "codexBuildFlavor": "prod",
  "codexBuildNumber": "2620"
}

---

rg 'electron-workspace-root-labels' openai-codex-src
rg 'electron-saved-workspace-roots' openai-codex-src
rg 'thread-workspace-root-hints' openai-codex-src
rg 'project-order' openai-codex-src

---

electron-workspace-root-labels      old root -> new display label
electron-saved-workspace-roots      still contains old root
project-order                       still contains old root
thread-workspace-root-hints         can still point at old/projectless roots

---

~/Documents/New project 3 -> Hermes agent
~/Documents/New project 4 -> Pinchpoint gateway
~/Documents/New project 5 -> Vera Companion app

---

{
  "electron-workspace-root-labels": {
    "/Users/<user>/Documents/New project 3": "Hermes agent",
    "/Users/<user>/Documents/New project 4": "Pinchpoint gateway",
    "/Users/<user>/Documents/New project 5": "Vera Companion app"
  },
  "electron-saved-workspace-roots": [
    "/Users/<user>/Documents/New project 5",
    "/Users/<user>/Documents/New project 4",
    "/Users/<user>/Documents/New project 3"
  ],
  "project-order": [
    "/Users/<user>/Documents/New project 5",
    "/Users/<user>/Documents/New project 4",
    "/Users/<user>/Documents/New project 3"
  ]
}

---

/Users/<user>/Documents/New project 3
/Users/<user>/Documents/New project 4
/Users/<user>/Documents/New project 5

---

/Users/<user>/Documents/New project 3  2 threads
/Users/<user>/Documents/New project 4  3 threads
/Users/<user>/Documents/New project 5  9 threads

---

/Users/<user>/Documents/New project 3  13 session_meta/turn_context cwd records
/Users/<user>/Documents/New project 4  17 session_meta/turn_context cwd records
/Users/<user>/Documents/New project 5  222 session_meta/turn_context cwd records

---

RolloutItem::SessionMeta(_) | RolloutItem::TurnContext(_) => true,
...
if !meta_line.meta.cwd.as_os_str().is_empty() {
    metadata.cwd = meta_line.meta.cwd.clone();
}
...
if metadata.cwd.as_os_str().is_empty() {
    metadata.cwd = turn_ctx.cwd.clone();
}

---

async fn read_thread_preserves_rollout_cwd_when_sqlite_metadata_exists() {
    ...
    let rollout_cwd = PathBuf::from("/");
    ...
    builder.cwd = home.path().join("sqlite-workspace");
    ...
    assert_eq!(thread.cwd, rollout_cwd);
}

---

pub struct ThreadMetadataUpdateParams {
    pub thread_id: String,
    pub git_info: Option<ThreadMetadataGitInfoUpdateParams>,
}

---

let ThreadMetadataUpdateParams { thread_id, git_info } = params;
...
let Some(ThreadMetadataGitInfoUpdateParams { sha, branch, origin_url }) = git_info else {
    return Err(invalid_request("gitInfo must include at least one field"));
};
...
let patch = StoreThreadMetadataPatch {
    git_info: Some(StoreGitInfoPatch { ... }),
    ..Default::default()
};

---

oldRoot: absolute path
newRoot: absolute path
displayLabel: string
renameFolderOnDisk: boolean

---

/Users/<user>/Documents/New project 3 -> /Users/<user>/Documents/Hermes agent
/Users/<user>/Documents/New project 4 -> /Users/<user>/Documents/Pinchpoint gateway
/Users/<user>/Documents/New project 5 -> /Users/<user>/Documents/Vera Companion app

---

~/.codex/.codex-global-state.json
~/.codex/state_5.sqlite threads.cwd
~/.codex/sessions/**/rollout-*.jsonl session_meta.payload.cwd
~/.codex/sessions/**/rollout-*.jsonl turn_context.payload.cwd
the actual folders under ~/Documents

---

rollout files changed: 14
rollout cwd records changed: 252
old SQLite rows for renamed roots: 0

new SQLite rows:
  /Users/<user>/Documents/Hermes agent        2
  /Users/<user>/Documents/Pinchpoint gateway  3
  /Users/<user>/Documents/Vera Companion app  9
RAW_BUFFERClick to expand / collapse

Summary

Codex Desktop can put a project into a split state after using the project rename UI:

  • the sidebar display label changes
  • the underlying workspace root remains ~/Documents/New project N
  • the actual folder on disk remains ~/Documents/New project N
  • existing thread rows and rollout metadata continue to reference the old root

The visible result is a sidebar row that shows both the renamed project label and the old autogenerated folder name, for example:

Vera Companio...    New proj...

This was reproducible across multiple renamed projects in the same profile, not just one project.

Environment

Observed on:

Codex Desktop: 26.506.31421
Bundle id: com.openai.codex
Platform: macOS arm64

The public repository checked was openai/codex at:

2abdeb3 Read cached metadata for installed Git plugins (#20825)

Why I am filing this here

I am filing this in openai/codex because it is the public repo linked to Codex installs and it contains the app-server, protocol, thread-store, rollout, and state-db code that participate in this bug. However, the Desktop/Electron code that appears to own the project sidebar and global Desktop state does not appear to be present in this public repository.

The public repo README describes this repository primarily as the Codex CLI and points separately to the Desktop app experience:

<strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.
...
If you want the desktop app experience, run <code>codex app</code> or visit ...

The installed macOS Desktop app is a packaged Electron application. Extracting the shipped /Applications/Codex.app/Contents/Resources/app.asar shows this package metadata:

{
  "name": "openai-codex-electron",
  "productName": "Codex",
  "version": "26.506.31421",
  "main": ".vite/build/bootstrap.js",
  "codexBuildFlavor": "prod",
  "codexBuildNumber": "2620"
}

The Desktop-specific persisted state keys involved in this bug were present in ~/.codex/.codex-global-state.json and in the shipped Electron bundle, but I could not find them in the public repo checkout. These searches returned no matches in openai/codex:

rg 'electron-workspace-root-labels' openai-codex-src
rg 'electron-saved-workspace-roots' openai-codex-src
rg 'thread-workspace-root-hints' openai-codex-src
rg 'project-order' openai-codex-src

The missing keys matter because the observed split state is created around those Desktop-owned values:

electron-workspace-root-labels      old root -> new display label
electron-saved-workspace-roots      still contains old root
project-order                       still contains old root
thread-workspace-root-hints         can still point at old/projectless roots

So this issue likely needs Desktop/Electron source changes, not just Rust changes in this repo. If that source lives elsewhere, please route this to the Desktop owner. The public Rust code is still relevant because it explains why stale rollout cwd values can survive and reappear after a partial Desktop-side rename/remap.

Steps to reproduce

  1. Create or open several Codex Desktop projects that were auto-created as ~/Documents/New project N.
  2. Create at least one thread in each project.
  3. Rename the projects from the Desktop UI, for example:
~/Documents/New project 3 -> Hermes agent
~/Documents/New project 4 -> Pinchpoint gateway
~/Documents/New project 5 -> Vera Companion app
  1. Restart Codex Desktop.
  2. Observe the project sidebar.

Observed state before repair

The display labels were updated, but the durable roots were not.

{
  "electron-workspace-root-labels": {
    "/Users/<user>/Documents/New project 3": "Hermes agent",
    "/Users/<user>/Documents/New project 4": "Pinchpoint gateway",
    "/Users/<user>/Documents/New project 5": "Vera Companion app"
  },
  "electron-saved-workspace-roots": [
    "/Users/<user>/Documents/New project 5",
    "/Users/<user>/Documents/New project 4",
    "/Users/<user>/Documents/New project 3"
  ],
  "project-order": [
    "/Users/<user>/Documents/New project 5",
    "/Users/<user>/Documents/New project 4",
    "/Users/<user>/Documents/New project 3"
  ]
}

The folders on disk still existed under the old names:

/Users/<user>/Documents/New project 3
/Users/<user>/Documents/New project 4
/Users/<user>/Documents/New project 5

SQLite threads.cwd also still pointed at the old names:

/Users/<user>/Documents/New project 3  2 threads
/Users/<user>/Documents/New project 4  3 threads
/Users/<user>/Documents/New project 5  9 threads

Rollout JSONL metadata also contained old cwd values:

/Users/<user>/Documents/New project 3  13 session_meta/turn_context cwd records
/Users/<user>/Documents/New project 4  17 session_meta/turn_context cwd records
/Users/<user>/Documents/New project 5  222 session_meta/turn_context cwd records

Relevant public code

The state extraction code treats rollout session_meta and turn_context as thread metadata sources:

codex-rs/state/src/extract.rs

RolloutItem::SessionMeta(_) | RolloutItem::TurnContext(_) => true,
...
if !meta_line.meta.cwd.as_os_str().is_empty() {
    metadata.cwd = meta_line.meta.cwd.clone();
}
...
if metadata.cwd.as_os_str().is_empty() {
    metadata.cwd = turn_ctx.cwd.clone();
}

There is also an explicit regression test asserting that rollout cwd wins even when SQLite has a different cwd:

codex-rs/thread-store/src/local/read_thread.rs

async fn read_thread_preserves_rollout_cwd_when_sqlite_metadata_exists() {
    ...
    let rollout_cwd = PathBuf::from("/");
    ...
    builder.cwd = home.path().join("sqlite-workspace");
    ...
    assert_eq!(thread.cwd, rollout_cwd);
}

That behavior makes a partial rename/remap fragile. Updating only SQLite or only Desktop global state can still leave old rollout cwd values that are read back later.

The current public app-server metadata update API also does not appear to expose a workspace-root/cwd remap operation. ThreadMetadataUpdateParams only accepts git_info:

codex-rs/app-server-protocol/src/protocol/v2/thread.rs

pub struct ThreadMetadataUpdateParams {
    pub thread_id: String,
    pub git_info: Option<ThreadMetadataGitInfoUpdateParams>,
}

The request processor also requires gitInfo and builds a Git-only patch:

codex-rs/app-server/src/request_processors/thread_processor.rs

let ThreadMetadataUpdateParams { thread_id, git_info } = params;
...
let Some(ThreadMetadataGitInfoUpdateParams { sha, branch, origin_url }) = git_info else {
    return Err(invalid_request("gitInfo must include at least one field"));
};
...
let patch = StoreThreadMetadataPatch {
    git_info: Some(StoreGitInfoPatch { ... }),
    ..Default::default()
};

So, from the public code, there does not seem to be a supported app-server operation for "rename/remap this workspace root and all thread cwd metadata under it."

Expected behavior

Project rename should not leave display label and workspace root split.

At minimum, a project rename should do one of these:

  1. Rename only the display label and keep the folder/root label in the UI clearly separate.
  2. Rename/remap the actual workspace root and update all durable path-keyed metadata consistently.

If the Desktop UI presents this as a project rename for an auto-created New project N folder, I would expect option 2.

Suggested fix

Add one supported operation for workspace root rename/remap instead of treating label, root, folder, SQLite metadata, and rollout metadata as separate updates.

Suggested shape:

oldRoot: absolute path
newRoot: absolute path
displayLabel: string
renameFolderOnDisk: boolean

The operation should update, transactionally where possible:

  • Desktop saved workspace roots
  • project order
  • workspace root labels
  • thread workspace-root hints
  • filesystem folder name, if requested and safe
  • SQLite threads.cwd for threads under the old root
  • rollout metadata that can otherwise restore stale cwd

If rewriting old rollout JSONL is not desirable, an append-only canonical remap record or a state DB override that is guaranteed to win over stale rollout session_meta.cwd would also work. The important part is that restart/reload must not recover the old root.

Local repair that fixed the issue

Closing Codex Desktop and applying a complete remap fixed the UI after restart:

/Users/<user>/Documents/New project 3 -> /Users/<user>/Documents/Hermes agent
/Users/<user>/Documents/New project 4 -> /Users/<user>/Documents/Pinchpoint gateway
/Users/<user>/Documents/New project 5 -> /Users/<user>/Documents/Vera Companion app

The repair updated:

~/.codex/.codex-global-state.json
~/.codex/state_5.sqlite threads.cwd
~/.codex/sessions/**/rollout-*.jsonl session_meta.payload.cwd
~/.codex/sessions/**/rollout-*.jsonl turn_context.payload.cwd
the actual folders under ~/Documents

After repair:

rollout files changed: 14
rollout cwd records changed: 252
old SQLite rows for renamed roots: 0

new SQLite rows:
  /Users/<user>/Documents/Hermes agent        2
  /Users/<user>/Documents/Pinchpoint gateway  3
  /Users/<user>/Documents/Vera Companion app  9

No thread content was changed. Only workspace-root metadata was remapped.

Regression test

Suggested regression coverage:

  1. Create three projects with auto-generated folder names under ~/Documents/New project N.
  2. Create at least one thread in each project.
  3. Rename the projects from the Desktop UI.
  4. Restart Desktop.
  5. Assert that the sidebar does not show New project N for renamed projects.
  6. Assert that threads still appear under the renamed projects.
  7. Assert that SQLite threads.cwd and rollout-derived cwd metadata do not restore the old root after restart.

Related issues

This overlaps with existing path/remap reports, but it is not the same repro:

  • #15347: moving/remapping a workspace folder without losing history
  • #15448: symlink path vs realpath mismatch
  • #18483: symlink/realpath project split
  • #11019: moved project folder breaks threads

The distinct issue here is that Codex Desktop's own project rename UI can create the mismatch without the user manually moving the folder first.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Project rename should not leave display label and workspace root split.

At minimum, a project rename should do one of these:

  1. Rename only the display label and keep the folder/root label in the UI clearly separate.
  2. Rename/remap the actual workspace root and update all durable path-keyed metadata consistently.

If the Desktop UI presents this as a project rename for an auto-created New project N folder, I would expect option 2.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING