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
- 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)
- 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
- 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
- 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
- Verify .agents/plugins/marketplace.json exists at the archive root when producing artifacts
- Pin the manifest `name` to the expected constant and keep a non-empty plugins array plus plugins/ dir
- Run the marketplace validation unit tests in CI for any artifact-generation changes
- Keep the embedded marketplace as fallback when downloaded validation fails
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
- downloaded openai/plugins marketplace root mismatch
- embedded official remote plugin marketplace is invalid
- Responses 请求体不是 UTF-8:{error}
- openai/plugins marketplace download is too large: {} bytes
- zip entry escapes destination: {name}
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/053c1ec01298ba01.
Report an issue: GitHub.