Marketplace local plugin path "./" cannot reference the repository root

Resolved 💬 9 comments Opened Apr 8, 2026 by mmizutani Closed Jun 17, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Description

The resolve_plugin_source_path() function in codex-rs/core/src/plugins/marketplace.rs rejects every form of local plugin source path that resolves to the repository root directory (the marketplace root itself). This makes it impossible to register a plugin whose .codex-plugin/plugin.json and skills live at the top level of the repository.

This may be by design, but supporting "./" as a reference to the repo root is a natural use case — particularly for monorepos that keep a single top-level .codex-plugin/plugin.json alongside an existing skills/ directory.

Steps to reproduce

Directory layout:

<repo>/
  .agents/plugins/marketplace.json
  .codex-plugin/plugin.json
  skills/

marketplace.json:

{
  "name": "my-plugins",
  "interface": { "displayName": "My plugins" },
  "plugins": [
    {
      "name": "repo-root-plugin",
      "source": { "source": "local", "path": "./" },
      "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
      "category": "productivity"
    }
  ]
}

Result: The marketplace fails to load.

Every path variation that should resolve to the repo root is rejected:

| \path\ value | Error |
|---|---|
| \"./"\ | \local plugin source path must not be empty\ |
| \"."\ | \local plugin source path must start with './'\ |
| \"././"\ | \local plugin source path must stay within the marketplace root\ |
| \"./plugins/../"\ | \local plugin source path must stay within the marketplace root\ |

Log output in \~/.codex/log/codex-tui.log\:

WARN codex_core::plugins::marketplace: skipping marketplace that failed to load
  path=<repo>/.agents/plugins/marketplace.json
  error=invalid marketplace file \`<repo>/.agents/plugins/marketplace.json\`: local plugin source path must not be empty

Root cause

The validation in \resolve_plugin_source_path()\ (marketplace.rs L338-374) applies three sequential checks after stripping the \"./"\ prefix:

  1. Check 1 (L344): \path.strip_prefix("./")\ — input must start with \"./"\. Rejects \"."\.
  2. Check 2 (L350): The remainder after stripping must not be empty. Rejects \"./"\ because \"./".strip_prefix("./")\ yields \""\.
  3. Check 3 (L358-360): All \Path::components()\ must be \Component::Normal\. Rejects \"././"\ (contains \CurDir\) and \"./plugins/../"\ (contains \ParentDir\).

The function always requires at least one \Normal\ path component, so the resolved path is always a strict subdirectory of the marketplace root. No valid \path\ value can reference the root itself.

Proposed change

Treat an empty remainder (after stripping \"./"\) as a valid path that resolves to the marketplace root:

RawMarketplaceManifestPluginSource::Local { path } => {
    let Some(path) = path.strip_prefix("./") else {
        return Err(/* "must start with \`./\`" */);
    };

    // "./" means the marketplace root itself.
    if path.is_empty() {
        return Ok(marketplace_root_dir(marketplace_path)?);
    }

    let relative_source_path = Path::new(path);
    if relative_source_path
        .components()
        .any(|component| !matches!(component, Component::Normal(_)))
    {
        return Err(/* "must stay within the marketplace root" */);
    }

    Ok(marketplace_root_dir(marketplace_path)?.join(relative_source_path))
}

Current workarounds

A. Place the plugin in a subdirectory and symlink to the repo-root skills:

.codex/plugins/my-plugin/
  .codex-plugin/plugin.json
  skills -> ../../../skills     # symlink
{ "source": { "source": "local", "path": "./.codex/plugins/my-plugin" } }

This works but adds indirection and fragile symlinks that may break on some platforms.

B. Set "path": "../.." in .agents/plugins/marketplace.json

{
  "name": "ecc",
  "interface": {
    "displayName": "Everything Claude Code"
  },
  "plugins": [
    {
      "name": "ecc",
      "source": {
        "source": "local",
        "path": "../.."
      },
      ...
    }
  ]
}

https://github.com/affaan-m/everything-claude-code/blob/098b773c115364259abeb538cbc7a68fffabbf2c/.agents/plugins/marketplace.json#L1-L11

This works but introduces undesirable reference to the ancestor directories of the repository root.

Environment

  • Codex CLI: main branch at \80ebc80be\
  • OS: macOS (Darwin 25.4.0)

Comparison with Claude Code Marketplace/Plugins

Claude Code marketplace configs do permit a local plugin source path ./, allowing bundling the repo root /skills/ in a repo root Claude Code plugin:

<repo>/
  .claude-plugin/
    marketplace.json
    plugin.json
  skills/

Example:

{
  "name": "ecc",
  ...
  "plugins": [
    {
      "name": "ecc",
      "source": "./",
      ...
    }
  ]
}

https://github.com/affaan-m/everything-claude-code/blob/098b773c115364259abeb538cbc7a68fffabbf2c/.claude-plugin/marketplace.json#L15

{
  "name": "ecc",
  ...
  "skills": ["./skills/"],
  ...
}

https://github.com/affaan-m/everything-claude-code/blob/main/.claude-plugin/plugin.json

Proposed Changes

A possible change to relax the restrictions with respect to the local plugin path in the marketplace config:
https://github.com/openai/codex/compare/main...mmizutani:codex:fix/marketplace-root-plugin-path?expand=1

View original on GitHub ↗

9 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #16500

Powered by Codex Action

rayjasson98 · 2 months ago

Thanks for documenting this. Unfortunately, neither workaround works for me. I still have to create a dedicated plugin folder based on Codex docs. Is there a reason Codex doesn’t follow the same convention as Claude?

heliusjing · 2 months ago

I also encountered this problem.

caseychow-oai contributor · 1 month ago
caseychow-oai contributor · 1 month ago

Updated fix PR: https://github.com/openai/codex/pull/28771

Final behavior accepts repo-root local plugin paths . and ./, keeps "" invalid, and still rejects non-normal/traversal paths like ././, ./plugins/../, and ../....

caseychow-oai contributor · 1 month ago

Merged: https://github.com/openai/codex/pull/28771

The root-local marketplace fix is now landed.

_Sent from my Codex_

caseychow-oai contributor · 1 month ago

Per my Codex slop above 😅 this should be addressed in the next cut!

sophio-japharidze-sonarsource · 1 month ago

Did the fix work for you @mmizutani ? I still face an issue - it's adding the marketplace but it does not appear inside Codex session, and the plugin also is not being listed. 🤔

EDIT: I was testing on v0.141.0, and it does not yet include the fix! Sorry about that

mmizutani · 28 days ago

@sophio-japharidze-sonarsource
The fix #28771 has been released in v0.142.0.