BigPizzaV3/CodexPlusPlus · error

downloaded openai/plugins marketplace is invalid

Error message

downloaded openai/plugins marketplace is invalid

What it means

validate_openai_plugins_marketplace_root (crates/codex-plus-core/src/plugin_marketplace.rs:476) checks a freshly extracted marketplace tree: local_openai_curated_marketplace_root_from_root must find .agents/plugins/marketplace.json, parse it as JSON, see name == OPENAI_CURATED_MARKETPLACE, a non-empty plugins array, and a plugins/ directory at the root. Any miss returns None and this 'downloaded openai/plugins marketplace is invalid' error aborts the install. Note the inner helper also propagates read/parse errors with their own context, so this message specifically means 'layout/manifest check failed'.

Source

Thrown at crates/codex-plus-core/src/plugin_marketplace.rs:478

    let mut components = path.components();
    match components.next()? {
        Component::Normal(_) => {}
        _ => return None,
    }
    let mut relative = PathBuf::new();
    for component in components {
        match component {
            Component::Normal(value) => relative.push(value),
            Component::CurDir => {}
            _ => return None,
        }
    }
    (!relative.as_os_str().is_empty()).then_some(relative)
}

fn validate_openai_plugins_marketplace_root(root: &Path) -> anyhow::Result<()> {
    let marketplace = local_openai_curated_marketplace_root_from_root(root)?
        .ok_or_else(|| anyhow::anyhow!("downloaded openai/plugins marketplace is invalid"))?;
    if marketplace != root {
        anyhow::bail!("downloaded openai/plugins marketplace root mismatch");
    }
    Ok(())
}

fn validate_openai_curated_remote_marketplace_root(root: &Path) -> anyhow::Result<()> {
    let marketplace = local_openai_curated_remote_marketplace_root_from_root(root)?
        .ok_or_else(|| anyhow::anyhow!("embedded official remote plugin marketplace is invalid"))?;
    if marketplace != root {
        anyhow::bail!("embedded official remote plugin marketplace root mismatch");
    }
    Ok(())
}

fn local_openai_curated_marketplace_root_from_root(root: &Path) -> anyhow::Result<Option<PathBuf>> {
    let marketplace_path = root
        .join(".agents")

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Inspect the extracted tree (the staging dir under ~/.tmp/plugins) — check where marketplace.json actually landed; if it is one level deeper, the zip has an extra root folder (that shape instead trips error 38's root-mismatch path in some forks — here it means not found at all)
  2. Verify the manifest: .agents/plugins/marketplace.json must be valid JSON with name matching OPENAI_CURATED_MARKETPLACE and a non-empty plugins array, plus a plugins/ sibling directory
  3. Re-download from the official OPENAI_PLUGINS_ZIP_URL on a clean network path and retry — transient corruption and proxy HTML pages are common causes
  4. If upstream genuinely changed layout, update local_openai_curated_marketplace_root_from_root / expectations in plugin_marketplace.rs to the new shape (with tests) and fall back to the embedded copy meanwhile

Example fix

# before: zip has an unexpected extra top-level folder
marketplace.zip
└── openai-plugins-main/      # extra nesting
    └── .agents/plugins/marketplace.json   # not at extract root

# after: manifest directly at root
marketplace.zip
└── .agents/plugins/marketplace.json
└── plugins/
Defensive patterns

Strategy: validation

Validate before calling

// Validate the extracted tree shape before calling install
fn marketplace_tree_valid(root: &Path) -> bool {
    let manifest = root.join(".agents").join("plugins").join("marketplace.json");
    let Ok(text) = std::fs::read_to_string(&manifest) else { return false };
    let Ok(v) = serde_json::from_str::<serde_json::Value>(&text) else { return false };
    v.get("name").and_then(|n| n.as_str()).is_some()
        && v.get("plugins").and_then(|p| p.as_array()).is_some_and(|a| !a.is_empty())
        && root.join("plugins").is_dir()
}

Type guard

fn manifest_looks_like_marketplace(value: &serde_json::Value, expected_name: &str) -> bool {
    value.get("name").and_then(|n| n.as_str()) == Some(expected_name)
        && value.get("plugins").and_then(|p| p.as_array()).is_some_and(|a| !a.is_empty())
}

Try / catch

match validate_openai_plugins_marketplace_root(&staging) {
    Err(e) if e.to_string().contains("marketplace is invalid") => {
        tracing::warn!("downloaded marketplace failed layout check; keeping embedded copy");
        fallback_to_embedded_marketplace()?;
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: Installing a downloaded marketplace zip whose root lacks .agents/plugins/marketplace.json, has a manifest with the wrong `name`, an empty `plugins` list, or a missing plugins/ directory — e.g. upstream restructured the archive, the zip has an extra top-level folder, or the download served a different artifact.

Common situations: Upstream changing the archive layout between releases (extra nesting level, renamed manifest); a URL redirect serving an HTML error page that unzips partially; internal mirrors repacking the zip with a different root; version skew between an old core crate and a new artifact format.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/053c1ec01298ba01. Report an issue: GitHub.