zeroclaw-labs/zeroclaw · error · anyhow::Error
plugin archive contains multiple manifest.toml files
Error message
plugin archive contains multiple manifest.toml files
What it means
The mirror image of error 1415: find_manifest_dir found more than one manifest.toml in the extracted tree (root match plus recursive matches collected by collect_manifest_dirs). Because the install target is ambiguous, it bails rather than picking one.
Source
Thrown at src/plugin_registry.rs:241
.split(['/', '\\'])
.any(|component| component == "..")
}
fn has_windows_drive_prefix(raw_name: &str) -> bool {
let bytes = raw_name.as_bytes();
bytes.len() >= 2 && bytes[1] == b':' && bytes[0].is_ascii_alphabetic()
}
fn find_manifest_dir(root: &Path) -> Result<PathBuf> {
let mut matches = Vec::new();
if root.join("manifest.toml").is_file() {
matches.push(root.to_path_buf());
}
collect_manifest_dirs(root, &mut matches)?;
match matches.as_slice() {
[dir] => Ok(dir.clone()),
[] => bail!("plugin archive does not contain manifest.toml"),
_ => bail!("plugin archive contains multiple manifest.toml files"),
}
}
fn collect_manifest_dirs(dir: &Path, matches: &mut Vec<PathBuf>) -> Result<()> {
for entry in std::fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
if path.join("manifest.toml").is_file() {
matches.push(path.clone());
}
collect_manifest_dirs(&path, matches)?;
}
}
Ok(())
}
fn copy_zip_entry_capped<R, W>(View on GitHub (pinned to 88bb9c8533)
Solutions
- Package exactly one plugin per archive: the zip's tree must contain a single manifest.toml
- Exclude extra manifests when zipping (`zip -r p.zip . -x 'examples/*' 'vendor/*'`)
- If you meant to install several plugins, publish each as its own registry entry/archive
- Update the entry's sha256 after repacking
Example fix
# before: root manifest.toml + vendored/other-plugin/manifest.toml # after cd plugin-root zip -r ../p-0.1.0.zip . -x 'vendored/*' 'examples/*' # exactly one manifest
Defensive patterns
Strategy: validation
Validate before calling
// Count manifests before packaging/publishing (root + recursive, as the source does):
let matches: Vec<_> = walkdir::WalkDir::new(&pkg_root)
.into_iter().filter_map(Result::ok)
.filter(|e| e.file_name() == "manifest.toml")
.collect();
if matches.len() != 1 { anyhow::bail!("expected exactly one manifest.toml"); } Try / catch
// On 'multiple manifest.toml files', locate the extras via the same walk, // exclude them from the zip (vendored/, examples/), and republish one-plugin-per-archive.
Prevention
- One plugin per archive, always
- Exclude vendor/example trees in the zip command
- Audit monorepo release scripts for accidental multi-manifest zips
When it happens
Trigger: Installing an archive that bundles multiple plugin packages — e.g. a repo zip containing the plugin plus a vendored example/ or dependency plugin, each with their own manifest.toml.
Common situations: Zipping a monorepo/workspace with several plugins into one artifact; vendoring another plugin's source into your tree; build output that copies the manifest into two locations (root and dist/).
Related errors
- plugin archive does not contain manifest.toml
- plugin archive manifest name '{}' does not match registry na
- plugin archive manifest version '{}' does not match registry
- manifest missing [tool] name
- manifest missing [tool] description
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/fdd959397271dd29.
Report an issue: GitHub.