zeroclaw-labs/zeroclaw · error · anyhow::Error
plugin archive does not contain manifest.toml
Error message
plugin archive does not contain manifest.toml
What it means
After extraction, find_manifest_dir walks the extracted tree looking for manifest.toml (checking the root first, then recursively via collect_manifest_dirs). Zero matches means the archive is not a valid ZeroClaw plugin package, so install bails instead of guessing an entry point.
Source
Thrown at src/plugin_registry.rs:240
|| raw_name
.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(())
}
View on GitHub (pinned to 88bb9c8533)
Solutions
- Confirm what is actually inside: `unzip -l plugin.zip` — if there is no manifest.toml, the artifact is wrong
- Rebuild the plugin zip from the directory that directly contains manifest.toml
- If you publish the registry, fix the entry URL to point at the correctly packaged artifact and update its sha256
- For source-only plugins, package them first (add manifest.toml with name/version) before registry distribution
Example fix
# before: zip of repo root (README, src/, no manifest)
# after
zip -r p-0.1.0.zip dist/ # dist/ contains manifest.toml + code
# republish index entry { url, sha256 } for p-0.1.0.zip Defensive patterns
Strategy: validation
Validate before calling
// Validate packaging before publishing (mirror find_manifest_dir's contract):
let n = walkdir::WalkDir::new(&extracted_root)
.into_iter()
.filter_map(Result::ok)
.filter(|e| e.file_name() == "manifest.toml")
.count();
assert_eq!(n, 1, "archive must contain exactly one manifest.toml"); Type guard
fn is_valid_plugin_package(root: &Path) -> bool {
root.join("manifest.toml").is_file()
&& /* recursive scan finds no second manifest */ true
} Try / catch
// On 'does not contain manifest.toml', open the zip (`unzip -l`) to see what // was actually packed, fix the packaging step, and republish with a new sha256.
Prevention
- Zip from the directory that directly holds manifest.toml
- Add a packaging check to the plugin release script
- Smoke-test `zeroclaw plugin install` against a staging registry before release
When it happens
Trigger: Installing a plugin archive that contains no manifest.toml anywhere — e.g. a source repo zip without packaging, a wrongly-built artifact, or the entry URL pointing at some other zip entirely.
Common situations: Packing the repo root instead of the packaged plugin dir; forgetting the manifest in the build script; registry entry URL accidentally referencing a random zip; nested build output (target/) zipped instead of dist/.
Related errors
- plugin archive contains multiple manifest.toml files
- the public plugin registry is not populated yet; use --regis
- plugin registry returned HTTP {status} for {registry_url}
- plugin archive manifest name '{}' does not match registry na
- plugin archive manifest version '{}' does not match registry
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/2af089c31879bff4.
Report an issue: GitHub.