Skip to content

Desktop: full-CDP silently disabled when CODEX_HOME is a symlink #35310

Open

Description

@0xdevalias

Summary

Codex Desktop's Browser Developer Mode can show Enable full CDP access as enabled while silently omitting the cdp tab capability from both Browser backends when CODEX_HOME (~/.codex) is a symlink.

The trusted Browser runtime does receive the privileged nodeRepl.config bridge. However, its call to readToml("browser/config.toml") rejects with:

Codex TOML directory must not be a symlink

The Browser client catches that exception, replaces it with a generic internal disabled result, and omits the cdp capability. The settings UI continues to display the toggle as enabled because it reads the same config through a different path that succeeds.

Environment

  • Codex Desktop: 26.715.71837 (CFBundleVersion 5702)
  • Bundle ID: com.openai.codex
  • Bundled CLI: codex-cli 0.145.0-alpha.30
  • Platform: Darwin 25.3.0 arm64 arm / macOS 26.3 (25D125)
  • Browser bundled plugin: 26.715.71837
  • codex features list reports both in_app_browser and browser_use_full_cdp_access as stable/true

Reproduction

  1. Use a symlink for CODEX_HOME. In this reproduction:

    ~/.codex -> ~/.dotfiles/codex/codex.symlink
    
  2. In Codex Settings → Browser, enable Developer Mode / Enable full CDP access.

  3. Confirm ~/.codex/browser/config.toml contains:

    full_cdp_access_enabled = true
  4. Restart/reinitialize Browser Use and inspect the capabilities returned by agent.browsers.list().

Actual behavior

Both healthy, connected backends omit the tab-level cdp capability:

  • Chrome (extension): browser capability viewport; tab capability pageAssets only.
  • Codex In-app Browser (iab): browser capabilities visibility, viewport; tab capability pageAssets only.

The settings UI still shows full CDP access as enabled. No actionable error is shown, and codex doctor does not identify the rejected configuration.

Expected behavior

  • A symlinked CODEX_HOME should work, provided the resolved path remains within the intended user-controlled configuration directory and passes appropriate ownership/permission validation.
  • If symlinks truly cannot be supported safely, the settings UI should refuse the change or show a clear diagnostic instead of displaying an enabled state that the Browser runtime will silently reject.
  • codex doctor should report this configuration/runtime mismatch and include the underlying rejection reason.

Live diagnosis

I attached a temporary in-process debugger to the active Node REPL runtime and set breakpoints inside the trusted, hash-approved bundled Browser client.

The full-CDP gate calls these privileged methods in parallel:

nodeRepl.config.readToml("browser/config.toml")
nodeRepl.config.readRequirements()

Observed results:

[
  {
    "status": "rejected",
    "reason": {
      "name": "Error",
      "message": "Codex TOML directory must not be a symlink"
    }
  },
  {
    "status": "fulfilled",
    "value": {
      "requirements": null
    }
  }
]

This establishes that:

  1. The privileged bridge is present in trusted Browser code.

  2. Enterprise requirements are not disabling the feature.

  3. The TOML read fails specifically because ~/.codex is a symlink.

  4. The Browser client catches the exception and returns:

    Full CDP access could not be verified from browser config and enterprise policy.
    
  5. Capability enrichment consequently omits cdp.

The public/model-facing globalThis.nodeRepl intentionally lacks config, so checking globalThis.nodeRepl?.config from ordinary submitted code is not a valid test for whether the trusted privileged bridge exists.

UX and diagnostic problems

  • The UI reports the persisted setting, not whether the runtime can actually use it.
  • The actionable native error is swallowed and replaced with a generic disabled state.
  • A normal developer configuration layout is rejected without documentation or a diagnostic.
  • There is no codex doctor check for a symlinked CODEX_HOME or for an enabled-but-unavailable full-CDP capability.

Relevant evidence

Artifact SHA-256
/Applications/ChatGPT.app/Contents/Resources/app.asar 11292b6a04d8aef36c30940b94ce3a744844dc5a52797228fbebb87f8529f102
/Applications/ChatGPT.app/Contents/Resources/cua_node/bin/node_repl 280df9eed10a975c7d5db7ee418b7da4df6de79158968183d2572e78bac746d2
bundled browser-client.mjs 2793c6809f8a4c90923435aa3a6d1e6f8cabb145212f06328b860b076b899525

The exact symlink rejection string is present in the shipped native node_repl executable. I could not find that implementation or either exact error string in the public openai/codex source tree.

Related issues

Those issues may have different platform-specific causes. This report does not claim the symlink rejection explains either one, but the restricted-versus-privileged nodeRepl distinction and swallowed readToml exception may provide a deeper diagnostic path.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    configIssues involving config.toml, config keys, config merging, or config updates
    on Jul 25, 2026
  2. 0xdevalias commented on Jul 25, 2026

    @0xdevalias
    Author

    I also submitted this through the Codex Desktop in-app feedback flow.

    In-app feedback ID: 019f9755-e508-7d62-ae34-e7162c0f58b5

  3. AdamSpore commented on Sep 27, 2026

    @AdamSpore

    This report led to a verified repair of a related case on macOS desktop 26.924.22138. In my case, ~/.codex and ~/.codex/browser were real directories; only ~/.codex/browser/sessions was a symbolic link.

    The visible failure was broader than missing full CDP: basic page access in both Chrome and the built-in browser failed with “saved browser permissions could not be verified,” even though Chrome tab discovery worked. It persisted across new chats and separate projects.

    With Codex helping diagnose it, we backed up the sessions link and replaced it with a real directory containing all 50 existing session permission files, verified byte-for-byte by SHA-256. The original target and all permission entries were preserved, and the global config was unchanged. The exact request that failed immediately before this change succeeded immediately afterward, without restarting. Both browser backends passed a page-read, link-click, destination-read test.

    The current installed native reader still contains Codex TOML directory must not be a symlink. Unlike your investigation, we did not directly capture the suppressed live exception; the reader mechanism is inferred, while the before/after result is verified.

    I added the detailed test to #48573 (comment) (that report is Windows; our tested repair is macOS only). Diagnostics should check the session directory as well as CODEX_HOME, and report which path was rejected.

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

    appIssues related to the Codex desktop appbrowserbugSomething isn't workingconfigIssues involving config.toml, config keys, config merging, or config updates

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions