{"record":{"id":"0a07f26a948abd8b","repo":"Hmbown/CodeWhale","slug":"source-is-not-a-valid-plugin-bundle-error","errorCode":null,"errorMessage":"source is not a valid plugin bundle: {error}","messagePattern":"source is not a valid plugin bundle: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/plugins/install/stage.rs","lineNumber":100,"sourceCode":"}\n\n/// Copy a local bundle directory into staging. Symlinks anywhere in the\n/// source are rejected; a stale `.installed-from` marker is never copied so\n/// provenance always reflects *this* install.\npub(super) fn stage_local_copy(\n    source: &Path,\n    user_plugins_dir: &Path,\n    max_size: u64,\n) -> Result<StagedPlugin> {\n    // Validate the source first; this also rejects symlinked roots/manifests.\n    let manifest_path =\n        crate::plugins::agent_plugin::resolve_manifest_path(source).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"source is not a valid plugin bundle: no plugin.json, .claude-plugin/plugin.json, kimi.plugin.json, or plugin.toml\"\n            )\n        })?;\n    PluginManifest::validate_from_path(&manifest_path)\n        .map_err(|error| anyhow::anyhow!(\"source is not a valid plugin bundle: {error}\"))?;\n    let canonical_source = source\n        .canonicalize()\n        .with_context(|| format!(\"failed to resolve {}\", source.display()))?;\n    if let Ok(canonical_plugins) = user_plugins_dir.canonicalize()\n        && (canonical_source == canonical_plugins\n            || canonical_source.starts_with(&canonical_plugins))\n    {\n        bail!(\n            \"cannot install a bundle from inside the user plugins directory {}; \\\n             it is already in place\",\n            canonical_plugins.display()\n        );\n    }\n\n    let staged_path = fresh_staging_dir(user_plugins_dir)?;\n    let result = (|| -> Result<StagedPlugin> {\n        let mut budget = CopyBudget::default();\n        copy_bundle_regular_files(&canonical_source, &staged_path, max_size, &mut budget)?;","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/plugins/install/stage.rs#L82-L118","documentation":"The local source had a manifest, but `PluginManifest::validate_from_path` failed on it (malformed JSON/TOML, missing required fields, invalid types). The validator's message is embedded as `{error}`; the source bundle is rejected before staging.","triggerScenarios":"`stage_local_copy` (called by `install_inner`) calls `PluginManifest::validate_from_path(&manifest_path)` after locating the manifest and the validation returns an error.","commonSituations":"Editing plugin.json by hand and introducing a syntax error or missing required field, manifests from an older schema version, trailing commas in JSON, wrong value types.","solutions":["Fix the specific issue named in the embedded `{error}` detail.","Run `jq . plugin.json` (or a TOML linter) to catch syntax errors.","Compare against the current plugin manifest schema and add missing required fields.","Pull the latest plugin source — an outdated checkout may carry an outdated manifest."],"exampleFix":"// before: plugin.json (missing version, trailing comma)\n{ \"plugin\": { \"name\": \"my-plugin\", } }\n// after\n{ \"plugin\": { \"name\": \"my-plugin\", \"version\": \"1.0.0\" } }","handlingStrategy":"validation","validationCode":"try {\n  const raw = fs.readFileSync(path.join(src, 'plugin.json'), 'utf8');\n  const m = JSON.parse(raw);\n  if (!m.plugin?.name || !m.plugin?.version) throw new Error('missing plugin.name/version');\n} catch (e) {\n  throw new Error(`invalid plugin manifest in ${src}: ${e.message}`);\n}","typeGuard":"function manifestParses(p) {\n  try { const m = JSON.parse(fs.readFileSync(path.join(p, 'plugin.json'), 'utf8')); return !!m?.plugin?.name; }\n  catch { return false; }\n}","tryCatchPattern":"try {\n  await codewhale.plugins.install(srcDir);\n} catch (e) {\n  if (e.message.includes('source is not a valid plugin bundle:')) {\n    console.error('manifest invalid — see detail:', e.message);\n  } else throw e;\n}","preventionTips":["Validate the manifest locally (jq / schema check) before each install.","Avoid trailing commas and type mismatches when hand-editing plugin.json.","Keep the manifest schema up to date with the codewhale version you run."],"tags":["plugins","manifest","schema-validation","rust"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}