{"record":{"id":"69fd41d502108123","repo":"jdx/mise","slug":"nix-package-references-must-not-contain-control-characters","errorCode":null,"errorMessage":"Nix package references must not contain control characters","messagePattern":"Nix package references must not contain control characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/nix.rs","lineNumber":26,"sourceCode":"use serde_json::Value;\n\nuse super::{InstallOpts, PackageRequest, PackageState, PackageStatus, SystemPackageManager};\n\npub(crate) struct NixManager;\n\n/// Bootstrap accepts attribute paths and explicit flake references, not Nix\n/// expressions, store paths, or output selectors. Nix owns source resolution.\n#[derive(Debug)]\npub(crate) struct Installable<'a> {\n    pub source: &'a str,\n    pub attribute: &'a str,\n    pub explicit: bool,\n}\n\nimpl<'a> Installable<'a> {\n    pub(crate) fn parse(name: &'a str) -> Result<Self> {\n        if name.chars().any(char::is_control) {\n            bail!(\"Nix package references must not contain control characters\");\n        }\n        let (source, attribute, explicit) = match name.split_once('#') {\n            Some((source, attribute)) => {\n                if source.is_empty()\n                    || source.starts_with('-')\n                    || source.starts_with('.')\n                    || source.starts_with('/')\n                    || source\n                        .strip_prefix(\"path:\")\n                        .is_some_and(|p| !p.starts_with('/'))\n                {\n                    bail!(\n                        \"invalid Nix flake reference '{source}'; use a registry name, URL, or path:/absolute/path\"\n                    );\n                }\n                (source, attribute, true)\n            }\n            None => (\"nixpkgs\", name, false),","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/nix.rs#L8-L44","documentation":"`Installable::parse` validates every Nix package reference before it is passed to a `nix` command. Nix arguments are joined into shell-ish command lines and interpolated into `nix eval --expr` strings, so control characters (newlines, tabs, NUL, etc.) are rejected outright — they are never valid in a flake or attribute name and would corrupt or inject into the constructed expression.","triggerScenarios":"A package name in config contains a control character (e.g. an accidental newline from YAML/TOML line folding, a tab, or an escape sequence) and reaches `Installable::parse`, called from `install`, `upgrade`, and `matching_entries`.","commonSituations":"Multi-line TOML/YAML strings that silently fold a newline into the package name; generated configs from scripts that don't trim output; copy-paste from terminals introducing invisible characters.","solutions":["Inspect the package name in your config and remove the control character; re-type the name on a single line.","Trim/normalize generated config output before writing package names.","Add a lint step that rejects control characters in package names.","If a template or script emits package lists, validate with something like `name.chars().all(|c| !c.is_control())` before applying."],"exampleFix":"# before (folded string contains a newline)\npackages = [\"rip\\\ngrep\"]\n\n# after\npackages = [\"ripgrep\"]","handlingStrategy":"validation","validationCode":"fn valid_pkg_name(name: &str) -> bool {\n    !name.is_empty() && !name.chars().any(char::is_control)\n}\nassert!(valid_pkg_name(pkg));","typeGuard":"fn is_plain_str(s: &str) -> bool { s.chars().all(|c| !c.is_control()) }","tryCatchPattern":null,"preventionTips":["Avoid multi-line folded strings for package names in TOML/YAML","Trim script/generated output before writing config","Lint package names for control characters in CI"],"tags":["nix","validation","input-sanitization","control-characters"],"backgroundTag":"invalid-argument-value","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}