Skip to content

[Bug]: Codex shadow home check disables provider when Codex rewrites a linked file (cloud-config-bundle-cache.json) #26811

Open

Description

@lukelabonte

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

  1. Set up a Codex provider with a shadow home path (for example ~/.codex-account/work).
  2. Replace cloud-config-bundle-cache.json in the shadow home with a symlink to ~/.codex/cloud-config-bundle-cache.json.
  3. 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).
  4. 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.

Let me know if more info would help.

Activity

  1. chrisohearn commented on Nov 2, 2026

    @chrisohearn

    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.

  2. added 2 commits that reference this issue on Nov 12, 2026
    97442e8
    f9b7698
  3. SacredAI commented on Nov 14, 2026

    @SacredAI

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions