{"record":{"id":"6cd8f9a0e93c89a6","repo":"nikivdev/code","slug":"must-be-an-external-cli-source-directory-or","errorCode":null,"errorMessage":"{} must be an external CLI source directory or {} file","messagePattern":"(.+?) must be an external CLI source directory or (.+?) file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/external_cli.rs","lineNumber":458,"sourceCode":"    let meta = fs::metadata(path)\n        .with_context(|| format!(\"failed to read external CLI source {}\", path.display()))?;\n    if meta.is_dir() {\n        let manifest_path = path.join(MANIFEST_NAME);\n        if !manifest_path.is_file() {\n            bail!(\"{} does not contain {}\", path.display(), MANIFEST_NAME);\n        }\n        return Ok((path.to_path_buf(), manifest_path));\n    }\n\n    if meta.is_file() && path.file_name().and_then(|name| name.to_str()) == Some(MANIFEST_NAME) {\n        let source_root = path\n            .parent()\n            .unwrap_or_else(|| Path::new(\".\"))\n            .to_path_buf();\n        return Ok((source_root, path.to_path_buf()));\n    }\n\n    bail!(\n        \"{} must be an external CLI source directory or {} file\",\n        path.display(),\n        MANIFEST_NAME\n    )\n}\n\nfn link_record_path(id: &str) -> PathBuf {\n    link_records_dir().join(format!(\"{}.toml\", id))\n}\n\nfn link_records_dir() -> PathBuf {\n    config::global_config_dir().join(\"cli\").join(\"links\")\n}\n\nfn ensure_link_records_dir() -> Result<PathBuf> {\n    let dir = link_records_dir();\n    fs::create_dir_all(&dir).with_context(|| format!(\"failed to create {}\", dir.display()))?;\n    Ok(dir)","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/external_cli.rs#L440-L476","documentation":"The fallthrough case of `resolve_install_source_paths`: the given path is neither a directory containing a manifest nor a file named `MANIFEST_NAME`. The library cannot interpret it as an external CLI source and bails.","triggerScenarios":"Calling `install_external_cli_link` with a path that is a regular file whose name is not `MANIFEST_NAME`, or a non-directory/non-file entry (device, socket, broken symlink).","commonSituations":"Passing a README or binary instead of the manifest; passing a path with a typo so it resolves to the wrong file type; pointing at an arbitrary file inside the tool directory instead of the tool directory itself.","solutions":["Pass either the tool's source directory or the manifest file itself (named exactly `MANIFEST_NAME`).","Fix typos in the path.","Verify the path exists and check its type (`ls -la`).","If installing from a file, rename it to the expected manifest name or pass its parent directory."],"exampleFix":"// before\ninstaller.install_external_cli_link(Path::new(\"~/src/my-tool/README.md\"))?;\n// after\ninstaller.install_external_cli_link(Path::new(\"~/src/my-tool\"))?;","handlingStrategy":"validation","validationCode":"fn assert_path_is_source(path: &Path, manifest_name: &str) -> Result<(), String> {\n    match std::fs::metadata(path) {\n        Ok(m) if m.is_dir() && path.join(manifest_name).is_file() => Ok(()),\n        Ok(m) if m.is_file() && path.file_name().map_or(false, |n| n == manifest_name) => Ok(()),\n        _ => Err(format!(\"{} is not a tool dir or manifest file\", path.display())),\n    }\n}","typeGuard":null,"tryCatchPattern":"match installer.install_external_cli_link(&p) {\n    Err(e) if e.to_string().contains(\"must be an external CLI source\") => {\n        eprintln!(\"{e}; check the path points at a tool directory or manifest\");\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Only pass tool source directories or files named exactly `MANIFEST_NAME`.","Resolve and print the canonical path before installing to catch typos.","Never pass arbitrary files (docs, binaries) as install sources.","Verify path type with `ls -la` or `fs::metadata` first."],"tags":["external-cli","install","invalid-argument"],"backgroundTag":"invalid-path-argument","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}