{"record":{"id":"e204dcaa1cbc137a","repo":"nikivdev/code","slug":"external-cli-tool-not-found-under","errorCode":null,"errorMessage":"external CLI tool {} not found under {}","messagePattern":"external CLI tool (.+?) not found under (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/external_cli.rs","lineNumber":375,"sourceCode":") -> Result<ResolvedExternalCliTool> {\n    let mut matches = Vec::new();\n\n    for root in roots {\n        for candidate in candidate_manifest_paths(root) {\n            let manifest = read_manifest(&candidate)?;\n            if manifest.id == id {\n                matches.push(resolved_from_manifest(\n                    candidate,\n                    manifest,\n                    ExternalCliResolutionKind::DevRoot,\n                    None,\n                )?);\n            }\n        }\n    }\n\n    match matches.len() {\n        0 => bail!(\n            \"external CLI tool {} not found under {}\",\n            id,\n            roots\n                .iter()\n                .map(|path| path.display().to_string())\n                .collect::<Vec<_>>()\n                .join(\", \")\n        ),\n        1 => Ok(matches.remove(0)),\n        _ => {\n            let locations = matches\n                .iter()\n                .map(|tool| tool.manifest_path.display().to_string())\n                .collect::<Vec<_>>()\n                .join(\", \");\n            bail!(\n                \"multiple external CLI manifests matched {}: {}\",\n                id,","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/external_cli.rs#L357-L393","documentation":"`resolve_external_cli_tool_in_roots` searches the configured root directories for a manifest matching the requested tool id. When zero manifests match, it bails with this error listing all the root paths that were searched.","triggerScenarios":"Calling `resolve_external_cli_tool(id, roots)` (or `resolves_manifest_from_roots`) where no manifest file under any root declares `id = \"<requested id>\"`.","commonSituations":"Typo in the tool id; tool never installed/linked into a root; manifest placed in the wrong directory or with the wrong filename; roots configured to a path that doesn't contain the tool.","solutions":["Check the tool id spelling against the manifest's `id` field.","List the roots shown in the message and confirm the manifest exists under one of them with the expected filename (`MANIFEST_NAME`).","Install or link the tool into one of the roots (e.g. via the install/link flow).","Correct your roots configuration to include the directory containing the manifest."],"exampleFix":"// before\nlet tool = resolver.resolve_external_cli_tool(\"my-clii\", &roots)?;\n// after\nlet tool = resolver.resolve_external_cli_tool(\"my-cli\", &roots)?;","handlingStrategy":"validation","validationCode":"fn assert_tool_installed(id: &str, roots: &[PathBuf]) -> Result<(), String> {\n    for root in roots {\n        let found = walk_manifests(root)?.any(|m| m.id == id);\n        if found { return Ok(()); }\n    }\n    Err(format!(\"tool {id} not present under any of {:?}\", roots))\n}","typeGuard":null,"tryCatchPattern":"match resolver.resolve_external_cli_tool(id, &roots) {\n    Err(e) if e.to_string().contains(\"not found under\") => {\n        eprintln!(\"installing missing tool {id}...\");\n        installer.install(id)?;\n        resolver.resolve_external_cli_tool(id, &roots)\n    }\n    other => other,\n}","preventionTips":["Verify the tool id spelling against the manifest `id` field.","Run an install/link step before resolution in scripts and CI.","Keep roots configuration in one place and assert required roots exist at startup.","Smoke-test tool resolution as part of environment setup."],"tags":["external-cli","not-found","configuration"],"backgroundTag":"tool-not-found","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}