{"record":{"id":"9b1ab615f073e4aa","repo":"zeroclaw-labs/zeroclaw","slug":"manifest-missing-tool-description","errorCode":null,"errorMessage":"manifest missing [tool] description","messagePattern":"manifest missing \\[tool\\] description","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-hardware/src/loader.rs","lineNumber":182,"sourceCode":"        ::zeroclaw_log::record!(\n            WARN,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                .with_attrs(::serde_json::json!({\n                    \"manifest_path\": manifest_path.display().to_string(),\n                    \"error\": format!(\"{}\", e),\n                })),\n            \"hardware plugin manifest failed to parse\"\n        );\n        anyhow::Error::msg(format!(\"TOML parse error in tool.toml: {e}\"))\n    })?;\n\n    // Validate required fields — fail fast with a descriptive error.\n    if manifest.tool.name.trim().is_empty() {\n        anyhow::bail!(\"manifest missing [tool] name\");\n    }\n    if manifest.tool.description.trim().is_empty() {\n        anyhow::bail!(\"manifest missing [tool] description\");\n    }\n    if manifest.exec.binary.trim().is_empty() {\n        anyhow::bail!(\"manifest missing [exec] binary\");\n    }\n\n    // Validate binary path: must exist, be a regular file, and reside within plugin_dir.\n    let canonical_plugin_dir = plugin_dir.canonicalize().map_err(|e| {\n        ::zeroclaw_log::record!(\n            WARN,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                .with_attrs(::serde_json::json!({\n                    \"plugin_dir\": plugin_dir.display().to_string(),\n                    \"error\": format!(\"{}\", e),\n                })),\n            \"cannot canonicalize plugin dir\"\n        );\n        anyhow::Error::msg(format!(","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-hardware/src/loader.rs#L164-L200","documentation":"Second required-field check in `load_one_plugin`: `[tool].description` is empty after trimming. Every plugin must carry a non-empty, human-readable description because it feeds tool listings and help output, so a manifest without it is rejected with the exact field named. Parse errors were already handled separately, so this is purely a content validation failure.","triggerScenarios":"A `tool.toml` `[tool]` section with `name` present but `description` missing, empty, or whitespace-only.","commonSituations":"Minimal manifests written only to satisfy the name check; a description removed because it looked optional; leftover whitespace placeholder text.","solutions":["Add a one-line `description = \"...\"` under `[tool]`","Ensure the value is non-empty after trimming — blank or whitespace-only strings still fail"],"exampleFix":"# before\n[tool]\nname = \"read-sensors\"\n# after\n[tool]\nname = \"read-sensors\"\ndescription = \"Reads board sensors and returns JSON\"","handlingStrategy":"validation","validationCode":"fn required_fields_ok(m: &ToolManifest) -> Result<(), String> {\n    if m.tool.description.trim().is_empty() {\n        return Err(\"[tool] description is required\".into());\n    }\n    Ok(())\n}","typeGuard":"fn manifest_has_description(raw: &str) -> bool {\n    toml::from_str::<ToolManifest>(raw)\n        .map(|m| !m.tool.description.trim().is_empty())\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Include a real description when scaffolding a plugin — blank placeholders fail after trim","Lint manifests in CI for all required fields at once","Remember validation trims: whitespace-only values are treated as missing"],"tags":["hardware","plugin","manifest","toml","loader"],"backgroundTag":"manifest-missing-field","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}