You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Codex shadow home check disables provider when Codex rewrites a linked file (cloud-config-bundle-cache.json) #26811
Version: T3 Code (Nightly) 0.1.4-nightly.20261029.3853, macOS
What happened
After updating to this nightly, both of my Codex providers that use a shadow home (one per account) went to Disabled with this error:
Driver 'codex' failed to create instance: Cannot create Codex shadow home entry
'cloud-config-bundle-cache.json' because '~/.codex-account/<account>/cloud-config-bundle-cache.json'
already exists and is not a symlink.
The other account hit the same error for config.toml. Both worked fine on the previous nightly.
Cause
From what I can tell, the new ensureSymlink in CodexHomeLayout.ts throws CodexShadowHomeEntryConflictError for any shared entry that's a real file in the shadow home, unless it's in PRIVATE_ENTRY_NAMES, SHADOW_LOCAL_ENTRY_NAMES, or REPLACEABLE_SHARED_RUNTIME_ENTRIES. One stale file disables the whole provider.
This is related to #5817 (same check), but the trigger is different. That one is about new entries Codex adds later. Here the shadow entry starts out as a correct symlink, and Codex replaces it.
The catch is that Codex creates these files itself. It writes files like config.toml and cloud-config-bundle-cache.json atomically (write temp, then rename), and the rename replaces the symlink with a real file.
Repro
Set up a Codex provider with a shadow home path (for example ~/.codex-account/work).
Replace cloud-config-bundle-cache.json in the shadow home with a symlink to ~/.codex/cloud-config-bundle-cache.json.
Start the provider. It's detected, and Codex refreshes the cloud config bundle. The symlink is now a real file again (for me it happened about a minute after I created it).
Restart T3 Code or recheck providers. The provider is Disabled with the error above.
Re-linking by hand doesn't stick. The cache expires after an hour, so Codex keeps rewriting it.
Suggested fix
Add cloud-config-bundle-cache.json to PRIVATE_ENTRY_NAMES. It's tied to a specific ChatGPT account and workspace, so it probably shouldn't be shared between accounts anyway.
For other shared entries (like config.toml), I think a warning, or re-linking when the contents match, would be better than disabling the provider. Even if a strict check is preferred, the error could suggest a fix.
Another file that hits this: .codex-global-state.json.
Setup: T3 Code (Nightly) 0.1.7-nightly.20261102.4093, Linux (Arch), codex-cli 0.166.0, a second Codex instance with a T3-managed shadow home.
What happened: the provider went to Disabled with:
Driver 'codex' failed to create instance: Cannot create Codex shadow home entry '.codex-global-state.json'
because '~/.t3/userdata/providers/codex/<id>/shadow/.codex-global-state.json' already exists and is not a symlink.
Codex saves this file by writing ..codex-global-state.json.tmp-<ms>-<uuid> and renaming it over the path, so the rename replaced the link with a regular file. .codex-global-state.json.bak was replaced the same way. The shadow copy was newer than the shared one and had every key the shared copy had, plus 12 more entries under electron-persisted-atom-state.
On #23472: as I read it, that PR keeps the provider enabled here, but the shadow home then keeps its own .codex-global-state.json for good. So after the first save, this file quietly stops being shared between accounts. Two options that avoid both the failure and the silent fork:
If the file is meant to be shared: reconcile instead of failing. Keep the newer copy in the shared home, back up the other one, and restore the link. That gives the same result as a working link (last writer wins).
If it's meant to be per-account: add it to PRIVATE_ENTRY_NAMES, like models_cache.json, so that's explicit.
Workaround in the meantime: I'm running a small inotify watcher that does the reconcile-and-relink step for regular files that exist in both homes. It skips SQLite files and directories. Happy to share it if useful.
Another file that hits this: .credentials.json, Codex's file store for MCP OAuth tokens.
Setup: T3 Code nightly 0.1.10-nightly.20261113.4656 (systemd user service), headless Linux x64 (Ubuntu), codex-cli 0.169.0. Two Codex instances with shadow homes (~/.codex-t3/personal, ~/.codex-t3/work) over ~/.codex. One MCP server uses OAuth (Sentry).
What happened: after a server restart, both instances failed:
Failed to create provider instance { instanceId: 'codex_personal', driver: 'codex',
detail: "Cannot create Codex shadow home entry '.credentials.json' because '~/.codex-t3/personal/.credentials.json' already exists and is not a symlink." }
Failed to create provider instance { instanceId: 'codex', driver: 'codex',
detail: "Cannot create Codex shadow home entry '.credentials.json' because '~/.codex-t3/work/.credentials.json' already exists and is not a symlink." }
Two earlier restarts on the same machine failed the same way on auth.json.lock (already reported in #5817).
Trigger: with the default mcp_oauth_credentials_store (auto), Codex writes $CODEX_HOME/.credentials.json when it cannot use the OS keyring. Here the GNOME keyring was locked because no one was logged into the desktop. Codex writes the file by replacing it, which turns the shadow-home symlink into a regular file. With mcp_oauth_credentials_store = "file" (the workaround for a locked keyring, see #24300), every OAuth token refresh can do this again.
This is another case for handling rewritten linked files in general, instead of one filename at a time.
Posted by Claude Code (Opus 5.5) via T3 Code triage.
Activity
chrisohearn commentedon Nov 2, 2026
Another file that hits this:
.codex-global-state.json.Setup: T3 Code (Nightly)
0.1.7-nightly.20261102.4093, Linux (Arch), codex-cli 0.166.0, a second Codex instance with a T3-managed shadow home.What happened: the provider went to Disabled with:
Codex saves this file by writing
..codex-global-state.json.tmp-<ms>-<uuid>and renaming it over the path, so the rename replaced the link with a regular file..codex-global-state.json.bakwas replaced the same way. The shadow copy was newer than the shared one and had every key the shared copy had, plus 12 more entries underelectron-persisted-atom-state.On #23472: as I read it, that PR keeps the provider enabled here, but the shadow home then keeps its own
.codex-global-state.jsonfor good. So after the first save, this file quietly stops being shared between accounts. Two options that avoid both the failure and the silent fork:PRIVATE_ENTRY_NAMES, likemodels_cache.json, so that's explicit.Workaround in the meantime: I'm running a small inotify watcher that does the reconcile-and-relink step for regular files that exist in both homes. It skips SQLite files and directories. Happy to share it if useful.
fix(server): shadow homes keep the Codex files Codex replaces by rename
fix(server): a real config.toml in a Codex shadow home no longer disa…
SacredAI commentedon Nov 14, 2026
Another file that hits this:
.credentials.json, Codex's file store for MCP OAuth tokens.Setup: T3 Code nightly
0.1.10-nightly.20261113.4656(systemd user service), headless Linux x64 (Ubuntu), codex-cli 0.169.0. Two Codex instances with shadow homes (~/.codex-t3/personal,~/.codex-t3/work) over~/.codex. One MCP server uses OAuth (Sentry).What happened: after a server restart, both instances failed:
Two earlier restarts on the same machine failed the same way on
auth.json.lock(already reported in #5817).Trigger: with the default
mcp_oauth_credentials_store(auto), Codex writes$CODEX_HOME/.credentials.jsonwhen it cannot use the OS keyring. Here the GNOME keyring was locked because no one was logged into the desktop. Codex writes the file by replacing it, which turns the shadow-home symlink into a regular file. Withmcp_oauth_credentials_store = "file"(the workaround for a locked keyring, see #24300), every OAuth token refresh can do this again.This is another case for handling rewritten linked files in general, instead of one filename at a time.
Posted by Claude Code (Opus 5.5) via T3 Code triage.