File Access Permission Selector Stuck Loading

Open 💬 3 comments Opened May 28, 2026 by denixciao-sketch

no-active-thread-019e70e7-04d8-7212-8f52-0c90ef5db11c

Summary
The file access permission selector button is permanently stuck in a loading/spinner state across ALL conversations. This completely blocks the chat input — no messages can be sent, no commands can be issued, no work can be done. The entire application is a $200/month paperweight right now.

My subscription is active. My disk has 13GB free. macOS permissions are correct. Code signing is valid. There is nothing wrong on my end. Through deep code analysis of the asar bundle, I've traced the issue to a deadlock in the permission mode initialization pipeline.

Impact
I cannot overstate how destructive this bug is. This is not a minor inconvenience — this is a total application failure.

I have critical production projects that are blocked from launching because Codex is completely non-functional. Every hour this persists is real money lost and real deadlines missed.

I've spent hours diagnosing this myself — extracting the asar bundle, decompiling the minified JS, tracing the permission mode state machine, analyzing Statsig feature flag initialization, reading SQLite logs, and inspecting IPC handlers — because the application gave me ZERO diagnostic information. Just a silent infinite spinner.

Reproduction
Open Codex (any workspace, any project)
Look at the file access permission selector (the dropdown/button near the chat input)
It shows a loading spinner indefinitely
Try to type and send any message → impossible, input is blocked
Switch to a different conversation → same problem
Create a new conversation → same problem
Restart Codex → same problem persists
This affects 100% of conversations, not just one.

Environment & Diagnostic Details
Config (~/.codex/config.toml)
toml

sandbox_mode = "danger-full-access"
approval_policy = "never"
approvals_reviewer = "user"
The most permissive configuration possible — yet the permission selector can't even finish loading.

Entitlements (verified via codesign -d --entitlements)

com.apple.security.app-sandbox = false ← NOT sandboxed
com.apple.security.files.user-selected.read-write = true
com.apple.security.network.client = true
com.apple.security.automation.apple-events = true
com.apple.security.cs.allow-jit = true
com.apple.security.cs.allow-unsigned-executable-memory = true
All correct. No macOS-level permission issue.

Code Signature

codesign --verify --deep --strict: PASSED
Notarization: stapled
Authority: Developer ID Application: OpenAI OpCo, LLC (2DC432GLL2)
Hardened Runtime: enabled
System State
Disk: 13GB available (NOT full)
No quarantine flags (xattr clean)
No sandbox violations in system logs
No TCC denials in unified log
All Codex processes running normally (main, GPU, renderer, app-server instances)
SIP: enabled
Gatekeeper: enabled
Running Processes (at time of failure)

/Applications/Codex.app/Contents/MacOS/Codex ← main process
/Applications/Codex.app/Contents/Resources/codex app-server ← primary app-server
/Applications/Codex.app/Contents/Resources/codex app-server --listen stdio:// ← multiple instances
/Applications/Codex.app/Contents/Frameworks/Codex Helper (Renderer) ← renderer
/Applications/Codex.app/Contents/Frameworks/Codex Helper (GPU) ← GPU process
Multiple node_repl and SkyComputerUseClient instances
Everything appears to be running — yet the UI is completely stuck.

Large Data Files (potential contributors)

644 MB ~/.codex/logs_2.sqlite
91 MB ~/.codex/logs_2.sqlite-wal
5.4 MB ~/.codex/state_5.sqlite
4.0 MB ~/.codex/state_5.sqlite-wal
66 KB ~/.codex/.codex-global-state.json
The logs_2.sqlite is 735 MB combined. If any startup or permission-loading logic queries this database synchronously, this could absolutely cause the loading hang.

What I've Already Tried
✅ Verified all macOS permissions (entitlements, code signing, notarization, TCC)
✅ Checked disk space (13GB free — not the issue)
✅ Restarted Codex
✅ Verified config.toml is set to maximum permissions
✅ Checked system logs for sandbox violations (none)
✅ Confirmed no quarantine flags
✅ Tried multiple workspaces and conversations
✅ Cleaned temp files
Root Cause (from code analysis of the asar bundle)
Through decompilation and analysis of the minified source code, I've traced the exact blocking chain:

  1. Submit button is blocked by isAgentModePending

In composer-EYkAbDY0.js, the submit logic calls hv() which computes:

javascript

disableSubmitForPendingPermissionsMode:
e.isAgentModePending ||
(e.agentMode !== 'read-only' && e.isWindowsSandboxRequirementPending && !e.isWindowsSandboxSetupPending)
When isAgentModePending is true, the entire chat input is blocked.

  1. isAgentModePending comes from use-permissions-mode-BBZBxA7Q.js

The B() (exported as usePermissionsMode) function computes:

javascript

let Z = B == null && ae == null && (h || b.isConfigDataPending);
// Z becomes isAgentModePending
So if isConfigDataPending is true, the mode is stuck pending.

  1. isConfigDataPending comes from ne() function

The ne() function computes:

javascript

let N = h || D || w || M;
// Where:
// D = isPending from requirements query (IPC: get-config-requirements-for-host)
// w = isPending from effective config query
// M = j && n(k ?? undefined) !== false && g === undefined
// g = statsig_default_enable_features (from shared object store)
If ANY of these remain pending, the entire permission selector stays in loading state forever.

  1. The statsig_default_enable_features dependency

When g === undefined (Statsig feature flags not yet loaded), AND the config requirements suggest guardian mode is available (j is true), then M stays true → isConfigDataPending stays true → isAgentModePending stays true → submit is blocked.

This creates a hard dependency on Statsig initialization for the permission selector. If Statsig fails to initialize (network issues, API changes, rate limiting, corrupted cache), the entire application becomes unusable.

  1. Observed state in ~/.codex/.codex-global-state.json

json

"codexCloudAccess": "enabled_needs_setup"
"agent-mode-by-host-id": { "local": "full-access" }
The codexCloudAccess: "enabled_needs_setup" state may also be contributing to the loading loop.

Why This Is Unacceptable
The permission selector blocks ALL chat input. A UI component that displays a mode dropdown should NEVER be able to make the entire application unusable.
No error, no fallback, no timeout. If Statsig or the backend config query fails, the app should fall back to config.toml settings (which are danger-full-access + approval_policy = never).
Zero diagnostic information. No console error, no log entry, no UI message. Just a silent spinner.
config.toml has sandbox_mode = "danger-full-access" and approval_policy = "never" — the local configuration is correct and should be usable as a fallback.
I wasted an entire day reverse-engineering your minified JavaScript to find this. A single error message or timeout would have prevented all of this.
What I Need
Never let the permission selector block the entire chat input. If permission state can't be resolved, show an error inline or use a default — don't freeze the entire application.
Add a timeout/fallback for isConfigDataPending. If the config requirements query or Statsig initialization doesn't complete within N seconds, fall back to config.toml settings and show a warning.
Show a clear error when the permission selector can't load. A toast, a banner, anything — not a silent infinite spinner.
Add better diagnostic logging. When isConfigDataPending remains true for more than 10 seconds, log which specific sub-condition is stuck (Statsig? Config query? Requirements query?).
Make the codexCloudAccess: "enabled_needs_setup" state actionable. If setup is needed, show a setup flow. Don't just silently block everything.
Closing
I chose Codex as my primary development tool and have invested significantly in building my workflow around it. Having it completely break with no workaround and no error message — just a silent infinite spinner — is unacceptable for a product at this price point. I expect this to be treated as a P0 blocker and resolved urgently.

Diagnostic data collected at 2026-05-29T07:00 CST (UTC+8)

View original on GitHub ↗

3 Comments

sulmusic2-star · 2 months ago

Confirming this exact bug on macOS 26.527.60818 (bundled CLI/app-server 0.136.0-alpha.2, Apple Silicon, ChatGPT Plus), and adding a decisive test + mechanism that I think pins the root cause. Cross-refs: this is the same underlying failure as #21318 (MCP tool-discovery blocks the first turn) and the macOS cluster in #23979. @denixciao-sketch's decompilation here matches what I see end to end.

Symptom

Local-mode composer: the "Full access" permission selector sits in a perpetual loading/greyed state with a ⚠ icon; the send arrow spins forever and no local message can be sent; past local thread bodies render blank. Cloud mode works fine (it routes around the local app-server). Intermittent across identical builds — it worked previously with the exact same plugins/connectors installed, then stopped without any config change on my side.

Decisive test: it is NOT user config / connectors / plugins

I stripped the install to zero enabled plugins (every [plugins.*] enabled = false, config locked with chflags uchg so the desktop couldn't re-sync it) and relaunched. Send still spun. With nothing for the tool list to wait on, the composer is still blocked. So this is not fixable from the user side — it's in the app/app-server.

Mechanism (combining @denixciao-sketch's decompilation with app-server traces)

The composer's submit is gated on isConfigDataPending, which stays true while either:

  1. list_all_tools hasn't completed — the app-server awaits tools from every MCP server on the startup/first-turn critical path (exactly #21318). Trace from logs_2.sqlite:

``
list_all_tools{mcp_server_count=5}: waiting for MCP server tools while building tool list
server_name=cloudflare-api has_cached_tool_info_snapshot=false startup_complete=true
`
has_cached_tool_info_snapshot is the key field: when a server has a cached snapshot the list returns instantly; when it's false, the app-server **waits for a live tools/list** — and a slow/unauthenticated/unreachable server (e.g. a connector whose OAuth token has since expired) hangs the whole list until the 31s timeout. **This explains the intermittency and "it worked before":** when the snapshots were warm / tokens valid, list_all_tools` returned fast; once a snapshot is cold and that server doesn't answer, it hangs.

  1. Statsig hasn't loaded — statsig_default_enable_features === undefined with guardian mode available keeps isConfigDataPending true (@denixciao-sketch's chain). My ~/.codex/.codex-global-state.json has 0 statsig keys cached. Clearing the statsig plugin cache doesn't help (the value comes from the live SDK init, not a file). guardian_approval is server-pushed and ignores features.guardian_approval = false in config — so there is no user override for this branch either.

Even the hardcoded openai-bundled marketplace (browser/chrome/computer-use/latex) is registered via the node_repl sidecar on every launch regardless of plugin state, and its marketplace/add times out the same way:

bundled_plugins_marketplace_add_failed errorMessage="Timed out waiting for MCP response to marketplace/add"

node_repl itself is healthy — the fault is the app-server driving it

Driving the bundled node_repl directly over stdio, it answers initialize in <1s and executes a real js tool call (spawning its sandboxed Node kernel) in ~0.25s, returning valid results — both sandboxed and with --disable-sandbox. When the app-server spawns it, the process sits parked in tokio::park waiting for MCP traffic and is torn down ~3s later. So node_repl is fine; the app-server's sidecar/tool-discovery lifecycle is where it breaks.

The fix that would actually resolve this (same as @denixciao-sketch + #21318)

  1. Never let MCP tool-discovery / Statsig block the composer. If isConfigDataPending can't resolve within N seconds, fall back to config.toml (mine is already sandbox_mode = "danger-full-access", approval_policy = "never") and let the user send. A silent infinite spinner with no error/timeout is the core defect.
  2. Make optional MCP servers non-blocking (background tool discovery, incremental availability) per #21318 — only required = true servers should ever block startup/first turn.
  3. Surface a diagnostic when isConfigDataPending stays true >10s (which sub-condition is stuck: requirements query / Statsig / a specific MCP server).

Happy to provide a RUST_LOG=trace app-server log slice from a failing launch, the zero-plugin-locked-config repro, the lsof/sample of the parked node_repl, or the node_repl stdio handshake repro. Also tracked via OpenAI support case 09438125.

sulmusic2-star · 2 months ago

Update: still fully broken on 26.601.20914 (build 3497) — the latest auto-update as of 2026-06-02 (macOS, Apple Silicon). I updated specifically to test whether this was addressed; it is not. The failure is byte-for-byte identical to 26.527.60818 and 26.519.31651 before it.

App-server log, one session on 26.601:

  • marketplace/add → Timed out waiting for MCP response to marketplace/add (×14), bundled_plugins_marketplace_add_failed for marketplaceName=openai-bundled
  • experimentalFeature/list → timed out (this is the call the composer's send path waits on: "Failed to check workspace dependencies feature for developer instructions")
  • config/read, configRequirements/read, collaborationMode/list → succeed in 5–25ms only when plugins + the node_repl-dependent [features] (memories/chronicle/goals) are disabled (which makes the app log reconcile_skipped_features_unavailable and skip the hanging reconcile). With them enabled, config/read times out and the "Full access" chip spins forever.
  • 0 messages dispatched all session — clicking send yields Unable to send message / Loading… and never reaches the backend, in both a new chat and an existing local thread.

Isolation done this round: from a completely clean slate — killed 27 lingering Codex processes including an orphaned openai-bundled/chrome extension-host parented to a dead app instance (these survive normal relaunch; the new launch immediately respawns one) — marketplace/add still times out. So it is not stale state or orphaned helpers. The bundled marketplace MCP handshake itself does not complete; the sidecar that should answer marketplace/add / experimentalFeature/list is gone seconds after launch, while node_repl is provably healthy when driven directly over stdio (initialize <1s, a real js tool call ~0.25s).

Net: the composer's send is hard-gated on MCP calls (experimentalFeature/list, marketplace/add) that depend on a bundled sidecar that never answers — with no timeout and no fallback, so it spins indefinitely. The CLI (codex exec/TUI) sends fine on the same build because it bypasses this path.

The fix, restated (matches @denixciao-sketch + #21318):

  1. Never let bundled-marketplace marketplace/add or experimentalFeature/list block the composer/first turn — make optional MCP startup non-blocking and incremental.
  2. Add a timeout + fallback to config.toml when isConfigDataPending can't resolve within N seconds, instead of an infinite silent spinner.
  3. Surface which sub-condition is stuck after ~10s.

Also tracked via OpenAI support case 09438125. Happy to provide the full RUST_LOG=trace slice, the clean-slate repro, or the orphaned-extension-host ps capture.

sulmusic2-star · 2 months ago

Workaround found — and it pinpoints the exact culprit. On 26.601.20914 I traced the composer-send block to the bundled-marketplace handshake. The local app-server sidecar startup and marketplace/add hang specifically on the heavy bundled plugins (browser, chrome, computer-use); the lightweight latex plugin adds instantly. The runtime marketplace is (re)written on startup and on every window focus with pluginCount=4 ["browser","chrome","computer-use","latex"], and marketplace/add then times out (observed 14×/session) — which blocks thread/start, so the composer can never dispatch.

Disabling just those bundled features via the CLI:

codex features disable browser_use
codex features disable browser_use_external
codex features disable computer_use
codex features disable in_app_browser

drops the runtime marketplace to ["latex"], after which marketplace/add succeeds and config/read, configRequirements/read, thread/start, and experimentalFeature/list all return in milliseconds (0 timeouts across a full session). The "Full access" selector settles and local send works end-to-end — message dispatches, a new conversation is created, and the agent executes workspace tools. Connector/MCP plugins can stay fully enabled — it is specifically the three bundled browser/chrome/computer-use plugins' MCP handshake that exceeds the (too-aggressive) catalog startup timeout.

This confirms the root cause discussed above: the composer is hard-gated on bundled-marketplace tool-discovery with no timeout and no fallback, and a single slow bundled plugin stalls the whole catalog. Requested fix stands:

  1. Make bundled/optional MCP startup non-blocking (incremental availability) so one slow plugin can't gate the composer/first turn.
  2. Add a timeout → fallback to config.toml when isConfigDataPending can't resolve in a few seconds, instead of an infinite silent spinner.
  3. Add per-bundled-plugin marketplace/add timeouts so browser/chrome/computer-use failing doesn't block latex or the send path.

Repro/environment: macOS 26.5.1, Apple M1 Ultra, Codex Desktop 26.601.20914 (app-server 0.136.0-alpha.2). Tracked in OpenAI support case 09438125.