{"record":{"id":"35299ac2e4b25b51","repo":"astrid-runtime/astrid","slug":"invalid-or-duplicate-executable-name-name","errorCode":null,"errorMessage":"invalid or duplicate executable name '{name}'","messagePattern":"invalid or duplicate executable name '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs.rs","lineNumber":845,"sourceCode":"            io::ErrorKind::InvalidInput,\n            \"executable replacement set must not be empty\",\n        ));\n    }\n    if !install_dir.is_dir() || !extract_dir.is_dir() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"executable replacement directories must exist\",\n        ));\n    }\n\n    let mut unique = HashSet::with_capacity(names.len());\n    for name in names {\n        let mut components = Path::new(name).components();\n        if !matches!(components.next(), Some(Component::Normal(_)))\n            || components.next().is_some()\n            || !unique.insert(*name)\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                format!(\"invalid or duplicate executable name '{name}'\"),\n            ));\n        }\n\n        let source = extract_dir.join(name);\n        let metadata = std::fs::symlink_metadata(&source).map_err(|error| {\n            io::Error::new(\n                error.kind(),\n                format!(\"release archive is missing '{name}': {error}\"),\n            )\n        })?;\n        if metadata.file_type().is_symlink() || !metadata.is_file() {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                format!(\"release executable is redirected or not regular: {name}\"),\n            ));\n        }","sourceCodeStart":827,"sourceCodeEnd":863,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs.rs#L827-L863","documentation":"Each name in `names` must be exactly one `Normal` path component — no leading `/`, no `..`, no nested paths like `bin/app` — and must appear only once in the set. `validate_replacement_inputs` rejects anything else (or a duplicate) with `InvalidInput`. This guards against path traversal and ambiguous double-installation of the same executable.","triggerScenarios":"Passing `\"bin/astrid\"`, `\"../astrid\"`, `\"/usr/bin/astrid\"`, `\"./astrid\"`, `\"\"`, or the same name twice in the `names` slice to `replace_executable_set`.","commonSituations":"Deriving names from archive paths that include subdirectories instead of stripping to the file name; a config listing the same executable twice; user-edited manifests containing absolute or relative paths; string joins producing empty entries.","solutions":["Pass bare file names only (e.g. `\"astrid\"`), and normalize any archive-relative paths with `Path::file_name()` before building the list","Deduplicate the list (e.g. via `HashSet` or `dedup()` after sorting) before calling","Reject or skip entries containing separators, `..`, or that are empty, at list-construction time"],"exampleFix":"// before\nlet names: Vec<&str> = entries.iter().map(|e| e.path.as_str()).collect(); // \"bin/astrid\"\n// after\nlet names: Vec<&str> = entries.iter()\n    .filter_map(|e| Path::new(e.path).file_name()?.to_str())\n    .collect::<HashSet<_>>().into_iter().collect();","handlingStrategy":"validation","validationCode":"fn sanitize_name(name: &str) -> Option<&str> {\n    let p = std::path::Path::new(name);\n    if p.components().count() == 1 && matches!(p.components().next(), Some(std::path::Component::Normal(_))) {\n        Some(name)\n    } else {\n        None\n    }\n}\nlet names: Vec<&str> = raw.into_iter().filter_map(sanitize_name).collect::<std::collections::HashSet<_>>().into_iter().collect();","typeGuard":"fn is_plain_file_name(name: &str) -> bool {\n    !name.is_empty() && std::path::Path::new(name).file_name() == Some(std::ffi::OsStr::new(name))\n}","tryCatchPattern":"match replace_executable_set(&install_dir, &extract_dir, &names) {\n    Err(e) if e.to_string().contains(\"invalid or duplicate executable name\") => {\n        eprintln!(\"malformed executable list: {e}\");\n    }\n    other => other.map_err(Into::into),\n}","preventionTips":["Strip archive paths to file_name() before building the names list","Deduplicate with a HashSet before calling","Reject entries containing '/', '\\\\', or \"..\" at input time","Validate manifests at load, not at install time"],"tags":["validation","invalid-input","path-traversal","self-update"],"backgroundTag":"invalid-identifier-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}