Summary
A malformed marketplace.json (with "source": "./") causes the plugin marketplace
sync subsystem to enter a tight retry loop. The loop is fast enough to starve the
event loop, after which the process pegs one core at 100% indefinitely and ignores
SIGTERM (only SIGKILL kills it). Log output stops several minutes before the user
notices, so there is no in-product signal that anything is wrong.
Symptoms
- One Codex process at 100% CPU on a single core for 35+ minutes
- Unresponsive to
kill <pid> (SIGTERM) — required kill -9
- No new entries in
~/.codex/log/codex-tui.log for the last ~18 minutes before kill,
despite the process being fully active
Root cause (from logs)
The last log entries before the silent loop show this sequence repeating ~every 3 seconds:
codex_core_skills::manager: skills cache cleared (1 entries)
codex_core_plugins::marketplace: skipping marketplace plugin that failed to resolve
path=~/.codex/.tmp/marketplaces/cloudflare/.claude-plugin/marketplace.json
plugin="cloudflare"
error=invalid marketplace file: local plugin source path must not be empty
codex_core_plugins::remote::remote_installed_plugin_sync: completed remote
installed plugin bundle sync ...
grep -c against the log shows 961 occurrences of these two messages before
logging fell behind / stopped.
The marketplace file in question is well-formed JSON; the offending field is:
{
"plugins": [
{ "name": "cloudflare", "source": "./", ... }
]
}
The resolver normalizes "./" to empty and rejects it. But the sync logic then
re-stages the marketplace bundle (~/.codex/.tmp/marketplaces/.staging had 78
recently-touched entries and 76 MB at time of kill), which appears to re-trigger
the watcher, which re-runs sync, which fails again — feedback loop.
Distinct bugs implied here
- Config error → unbounded retries with no backoff — a single un-resolvable
plugin shouldn't be re-tried 961+ times per session.
- File-watcher feedback loop — staging into a directory being watched re-fires
the watcher.
- Event-loop starvation in Rust core —
@openai/codex Node wrapper became
unresponsive to SIGTERM because the underlying Rust task held the runtime.
- Silent failure — log flushing fell behind the loop, so the last ~18 min of
the failure had zero diagnostic output.
Repro (theoretical, not re-tested)
Place any plugin in a marketplace where source: "./" (or any value that
normalizes to empty). Launch Codex. The retry loop should begin during startup
plugin sync.
Workaround
Stop Codex, then:
rm -rf ~/.codex/.tmp/marketplaces/.staging ~/.codex/.tmp/marketplaces/marketplace-backup-*
Editing the offending marketplace.json's "source" to an absolute path also
prevents the loop.
Environment
- macOS (Darwin 25.4.0, Apple Silicon)
- Codex (
@openai/codex), version per ~/.codex/version.json: 0.133.0
- Node runtime bundled with Codex
Summary
A malformed
marketplace.json(with"source": "./") causes the plugin marketplacesync subsystem to enter a tight retry loop. The loop is fast enough to starve the
event loop, after which the process pegs one core at 100% indefinitely and ignores
SIGTERM (only SIGKILL kills it). Log output stops several minutes before the user
notices, so there is no in-product signal that anything is wrong.
Symptoms
kill <pid>(SIGTERM) — requiredkill -9~/.codex/log/codex-tui.logfor the last ~18 minutes before kill,despite the process being fully active
Root cause (from logs)
The last log entries before the silent loop show this sequence repeating ~every 3 seconds:
grep -cagainst the log shows 961 occurrences of these two messages beforelogging fell behind / stopped.
The marketplace file in question is well-formed JSON; the offending field is:
{ "plugins": [ { "name": "cloudflare", "source": "./", ... } ] }The resolver normalizes
"./"to empty and rejects it. But the sync logic thenre-stages the marketplace bundle (
~/.codex/.tmp/marketplaces/.staginghad 78recently-touched entries and 76 MB at time of kill), which appears to re-trigger
the watcher, which re-runs sync, which fails again — feedback loop.
Distinct bugs implied here
plugin shouldn't be re-tried 961+ times per session.
the watcher.
@openai/codexNode wrapper becameunresponsive to SIGTERM because the underlying Rust task held the runtime.
the failure had zero diagnostic output.
Repro (theoretical, not re-tested)
Place any plugin in a marketplace where
source: "./"(or any value thatnormalizes to empty). Launch Codex. The retry loop should begin during startup
plugin sync.
Workaround
Stop Codex, then:
Editing the offending
marketplace.json's"source"to an absolute path alsoprevents the loop.
Environment
@openai/codex), version per~/.codex/version.json:0.133.0