macOS: same-version Chrome plugin cache reconciliation deletes chrome/latest while the plugin remains current
What version of the Codex App are you using (From “About Codex” dialog)?
ChatGPT desktop app 26.730.61639 (build 6234, bundle identifier com.openai.codex).
The bundled chrome@openai-bundled plugin is also version 26.730.61639.
What subscription do you have?
ChatGPT Pro
What platform is your computer?
~~~text
Darwin 25.5.0 arm64 arm
Google Chrome 150.0.7871.188
ChatGPT Chrome Extension 1.2.27236.6274_0
~~~
What issue are you seeing?
On macOS, a same-version bundled-plugin cache reconciliation can replace the entire Chrome plugin cache root and delete the runtime-created latest symlink, even though the current versioned plugin directory remains complete and the plugin is still reported as installed, enabled, and current.
Before reconciliation:
~~~text
~/.codex/plugins/cache/openai-bundled/chrome/
├── 26.730.61639/
└── latest -> 26.730.61639
~~~
After reconciliation, without an app or plugin version change:
~~~text
~/.codex/plugins/cache/openai-bundled/chrome/
└── 26.730.61639/
~~~
The versioned directory still exists, but latest is absent. The plugin manager still reports:
~~~text
chrome@openai-bundled installed, enabled 26.730.61639
~~~
The Chrome Native Messaging manifest remains syntactically valid and continues to point through the missing alias:
~~~text
/Users/<user>/.codex/plugins/cache/openai-bundled/chrome/latest/extension-host/macos/arm64/ChatGPT for Chrome
~~~
Therefore, once latest is deleted, a fresh native-host launch resolves to a nonexistent path. This can be temporarily masked when the native host and its communication channel are already running: an existing connection may continue controlling Chrome without resolving the manifest again.
Manually recreating the current-version symlink restores a fresh Chrome connection:
~~~sh
cd ~/.codex/plugins/cache/openai-bundled/chrome
ln -s 26.730.61639 latest
~~~
After this repair, Codex successfully opened https://example.com/ in the user's Chrome and read the page title and heading. A later bundled-plugin cache rebuild deleted the manually repaired symlink again, so the workaround is not durable.
This leaves three layers inconsistent:
- The complete versioned plugin directory exists.
- The plugin manager reports the plugin installed/enabled/current.
- The Native Messaging manifest points through a missing
latestalias.
What steps can reproduce the bug?
- Install and enable the bundled Chrome plugin and the ChatGPT Chrome Extension on macOS.
- Confirm that the cache contains both
chrome/26.730.61639/andchrome/latest -> 26.730.61639. - Confirm that the Chrome Native Messaging manifest points to
chrome/latest/.../ChatGPT for Chrome. - Resume/switch desktop tasks or open the plugin UI and allow the bundled-plugin background reconciliation to run. In the observed runs, all bundled plugin version directories were recreated together without a version change. The desktop log does not emit a dedicated cache-swap event, so the exact initiating UI request is not yet proven.
- Inspect the cache again.
- Observe that
chrome/26.730.61639/remains complete, butchrome/latesthas been deleted. - Observe that the plugin still appears installed, enabled, and current.
- Recreate
latestas a symlink to26.730.61639and establish a fresh Chrome connection. Chrome control works. - Allow another bundled-plugin reconciliation to run.
- Observe that the manually repaired
latestsymlink is deleted again.
This was observed repeatedly on the same desktop/plugin version; no application upgrade occurred between deletion events.
What is the expected behavior?
Bundled-plugin reconciliation should preserve or atomically recreate every runtime artifact required by the installed plugin.
- If Native Messaging manifests use
chrome/latest, that alias should always point to the active complete plugin version. - A plugin should not be reported as current when its registered native-host executable does not resolve to an existing executable.
- The versioned directory, active alias, Native Messaging manifest, and installed-plugin state should be reconciled as one recoverable transaction.
- A manually repaired current-version alias should not be removed by a same-version background refresh.
- Preferably, a long-lived native host should be registered from a stable location outside a replaceable plugin-cache root.
Additional information
Related but not duplicate
- #31904 reports a dangling
latestsymlink that still points to a removed old plugin version, together with a stale executable name after an update. - #32238 primarily reports Chrome for Testing/Chromium selection and also notes an alias still pointing to an old removed version.
- #32706 and #22114 report Windows file-lock/partial-update transaction failures.
- #24296 describes the broader Windows architectural risk of running the native host from a mutable plugin cache.
- #18863 covers symlinks inside plugin source trees being skipped during copying.
This report is specifically about macOS, standard Chrome, no version transition, and the latest sibling itself being deleted after a successful same-version plugin-root rebuild. The current version directory remains present and the plugin remains reported as current.
Possible source-level cause
This is a hypothesis based on the current public source, separate from the directly observed filesystem behavior:
replace_plugin_root_atomicallystages only the versioned plugin directory, then renames the existing complete plugin root to a backup and renames the staged root into place: https://github.com/openai/codex/blob/main/codex-rs/core-plugins/src/store.rs#L605-L697- The runtime-created sibling
latestalias is not included in that staged root. active_plugin_versionconsiders directory entries and can still find the versioned directory while ignoring the missing symlink: https://github.com/openai/codex/blob/main/codex-rs/core-plugins/src/store.rs#L169-L181
The filesystem deletion, incorrect current status, manifest path, temporary symlink repair, successful Chrome control after repair, and repeated deletion were directly observed. The exact background caller that initiated the successful root replacement is inferred from the simultaneous bundled-plugin rebuild and current Store implementation.
All usernames, workspace paths, prompts, account data, private URLs, logs, and task/session identifiers have been omitted.
8 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Thanks — I reviewed #36100. It appears related, but it does not cover the failure reported here, so I am keeping this issue open.
The two reports affect different layers:
outdatedand reinstalls them. The reported impact is a 30–40 second startup delay.installed, enabled, current, but the runtime-created siblingchrome/latestis absent. The Native Messaging manifest therefore resolves to a nonexistent host path. Recreating the symlink restores Chrome control, and a later reconciliation removes it again.#36100's variant mismatch may be one trigger that causes the replacement path to run repeatedly. However, that would make it an upstream trigger for this report, not the same correctness bug. Fixing the unnecessary
outdatedclassification would reduce how often this occurs, but any legitimate same-version replacement would still need to preserve or atomically recreate the runtime artifacts required by the Native Messaging registration.The invariant specific to this issue is:
For that reason, #36100 is related but not a duplicate of this issue.
Confirming an independent reproduction on macOS with standard Google Chrome.
Environment
26.803.61601(build6396)26.803.6160126.5.2arm64151.0.7922.761.2.27236.6274_00.147.0Observed state
The cache contains only the complete versioned plugin directory:
There is no
chrome/latestentry. Chrome's Native Messaging manifest still points to:That target does not exist, while the executable does exist and passes strict code-signature verification at:
Browser discovery therefore returns:
False-green diagnostic
check-native-host-manifest.js --browser chrome --jsonreports:The check validates the manifest contents but does not verify that the manifest's
pathresolves to an existing executable.Recovery attempted
A full supported clean reinstall did not restore
latest:The versioned plugin directory is recreated, but
latestremains absent and browser control remains unavailable.Two changes would make this failure actionable: preserve/atomically recreate
latestduring reconciliation, and make the manifest diagnostic require that the configured host path exists and is executable.This is really annoying and affecting multiple computers I own and also a peer's. It's obviously super widespread. @tibo-openai
Here's a deep research context on the issue:
Fresh reproduction on 2026-08-12 with the current macOS desktop/plugin release:
chrome@openai-bundled26.803.81509The bundled Chrome plugin was reinstalled and Chrome was explicitly invoked with
@chrome, but the Chrome backend remained unavailable:agent.browsers.list()exposed only the in-app browser.The bundled diagnostics reported Chrome running, the extension installed/enabled, and the Native Messaging manifest
correct: true. However, the manifest's configured executable resolved through:That path did not exist because
chrome/latestwas absent. The real versioned executable did exist and was executable at:The Chrome plugin cache root contained only
26.803.81509/; there was nolatestentry. No native-host process started when the extension was opened.This confirms the bug persists in 26.803.81509 and that
check-native-host-manifest.jscan still returncorrect: truewithout verifying thatmanifest.pathresolves to an existing executable. Reinstalling the bundled plugin did not restore the missing alias.I can reproduce this independently on a newer build, including after a clean uninstall/reinstall through the official UI.
Environment
26.803.61601(CFBundleVersion6396)chrome@openai-bundled26.803.61601151.0.7922.1091.2.27236.6274_0Clean reproduction
plugin_install_succeeded).Immediately after reinstall, the cache contained only:
There was no
latestsymlink/directory.The recreated manifest at:
pointed to:
That target does not exist. The signed executable does exist at the equivalent path under
26.803.61601/.I also found
extension-host-config.jsonabsent from the plugin cache.Runtime result
Browser is not available: chrome.Diagnostic false positive
The bundled
check-native-host-manifest.js --browser chrome --jsonreportscorrect: trueeven though the manifest's executable path does not exist. The checker appears to validate manifest identity/origins/path text but not that the resolved target exists and is executable.This narrows the current failure beyond later cache reconciliation: on
26.803.61601, the official reinstall flow itself can report success while creating the versioned directory and a manifest that depends on a missinglatestalias.Suggested acceptance criteria:
chrome/latestduring install and reconciliation.I did not apply a manual symlink workaround, so the state above is the unmodified result of the official reinstall flow.
Confirming a closely related variant: on this machine the breakage happens on every version change, not just same-version reconciliation — the updater installs the new versioned directory and deletes the old one, but never repoints
latest. Recurrence log (macOS arm64, Mac mini M4):latest-> 26.727.51351 (deleted); 26.730.61639 present → danglinglatest-> 26.730.61639 (deleted); 26.803.41515 present → danglinglatest-> 26.803.41515 (deleted); 26.803.61601 present → danglinglatest-> 26.810.41047 (deleted); 26.810.52044 present → dangling4/4 plugin updates stranded the symlink. Symptom each time:
Browser is not available: chromeeven though Chrome is running, the extension is enabled, and the native-host manifest is valid — because the manifest path resolves throughlatestand the spawn fails with ENOENT.Two extra findings that may help triage:
chrome-is-running.js,installed-browsers.js,check-extension-installed.js,check-native-host-manifest.js) exit 0 while the bridge is completely broken, becausecheck-native-host-manifest.jsvalidates the manifest's JSON fields but never checks thatpathexists on disk. Suggest adding an existence+executable check on the resolved host path.latest(unclear if fixed or lucky).Also worth noting two related failure modes seen while diagnosing (may deserve their own issues):
ChatGPT for Chromehost and the app-server, e.g. after sleep/wake). Killing the host process is sufficient — Chrome's extension reconnects and Chrome spawns a fresh host within seconds; the bridge self-heals with no app restart.Browser is not available: chromewhile fresh sessions work fine. Recovery requires resetting the session's JS runtime or starting a new thread; sessions should ideally re-bind automatically.Local workaround in use (self-healing watchdog): an hourly launchd job that verifies
latestresolves to a directory containing an executable host binary and repoints it to the newest valid cached version when stranded.