{"record":{"id":"2af089c31879bff4","repo":"zeroclaw-labs/zeroclaw","slug":"plugin-archive-does-not-contain-manifest-toml","errorCode":null,"errorMessage":"plugin archive does not contain manifest.toml","messagePattern":"plugin archive does not contain manifest\\.toml","errorType":"console","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/plugin_registry.rs","lineNumber":240,"sourceCode":"        || raw_name\n            .split(['/', '\\\\'])\n            .any(|component| component == \"..\")\n}\n\nfn has_windows_drive_prefix(raw_name: &str) -> bool {\n    let bytes = raw_name.as_bytes();\n    bytes.len() >= 2 && bytes[1] == b':' && bytes[0].is_ascii_alphabetic()\n}\n\nfn find_manifest_dir(root: &Path) -> Result<PathBuf> {\n    let mut matches = Vec::new();\n    if root.join(\"manifest.toml\").is_file() {\n        matches.push(root.to_path_buf());\n    }\n    collect_manifest_dirs(root, &mut matches)?;\n    match matches.as_slice() {\n        [dir] => Ok(dir.clone()),\n        [] => bail!(\"plugin archive does not contain manifest.toml\"),\n        _ => bail!(\"plugin archive contains multiple manifest.toml files\"),\n    }\n}\n\nfn collect_manifest_dirs(dir: &Path, matches: &mut Vec<PathBuf>) -> Result<()> {\n    for entry in std::fs::read_dir(dir).with_context(|| format!(\"reading {}\", dir.display()))? {\n        let entry = entry?;\n        let path = entry.path();\n        if path.is_dir() {\n            if path.join(\"manifest.toml\").is_file() {\n                matches.push(path.clone());\n            }\n            collect_manifest_dirs(&path, matches)?;\n        }\n    }\n    Ok(())\n}\n","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/src/plugin_registry.rs#L222-L258","documentation":"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.","triggerScenarios":"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.","commonSituations":"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/.","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"],"exampleFix":"# before: zip of repo root (README, src/, no manifest)\n# after\nzip -r p-0.1.0.zip dist/          # dist/ contains manifest.toml + code\n# republish index entry { url, sha256 } for p-0.1.0.zip","handlingStrategy":"validation","validationCode":"// Validate packaging before publishing (mirror find_manifest_dir's contract):\nlet n = walkdir::WalkDir::new(&extracted_root)\n    .into_iter()\n    .filter_map(Result::ok)\n    .filter(|e| e.file_name() == \"manifest.toml\")\n    .count();\nassert_eq!(n, 1, \"archive must contain exactly one manifest.toml\");","typeGuard":"fn is_valid_plugin_package(root: &Path) -> bool {\n    root.join(\"manifest.toml\").is_file()\n        && /* recursive scan finds no second manifest */ true\n}","tryCatchPattern":"// On 'does not contain manifest.toml', open the zip (`unzip -l`) to see what\n// was actually packed, fix the packaging step, and republish with a new sha256.","preventionTips":["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"],"tags":["plugin-registry","manifest","packaging","install"],"backgroundTag":"missing-plugin-manifest","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}