jdx/mise · error

Nix package references must not contain control characters

Error message

Nix package references must not contain control characters

What it means

`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.

Solutions

  1. Inspect the package name in your config and remove the control character; re-type the name on a single line.
  2. Trim/normalize generated config output before writing package names.
  3. Add a lint step that rejects control characters in package names.
  4. If a template or script emits package lists, validate with something like `name.chars().all(|c| !c.is_control())` before applying.

Example fix

# before (folded string contains a newline)
packages = ["rip\
grep"]

# after
packages = ["ripgrep"]
Defensive patterns

Strategy: validation

Validate before calling

fn valid_pkg_name(name: &str) -> bool {
    !name.is_empty() && !name.chars().any(char::is_control)
}
assert!(valid_pkg_name(pkg));

Type guard

fn is_plain_str(s: &str) -> bool { s.chars().all(|c| !c.is_control()) }

Prevention

When it happens

Trigger: 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`.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/69fd41d502108123. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/nix.rs:26

use serde_json::Value;

use super::{InstallOpts, PackageRequest, PackageState, PackageStatus, SystemPackageManager};

pub(crate) struct NixManager;

/// Bootstrap accepts attribute paths and explicit flake references, not Nix
/// expressions, store paths, or output selectors. Nix owns source resolution.
#[derive(Debug)]
pub(crate) struct Installable<'a> {
    pub source: &'a str,
    pub attribute: &'a str,
    pub explicit: bool,
}

impl<'a> Installable<'a> {
    pub(crate) fn parse(name: &'a str) -> Result<Self> {
        if name.chars().any(char::is_control) {
            bail!("Nix package references must not contain control characters");
        }
        let (source, attribute, explicit) = match name.split_once('#') {
            Some((source, attribute)) => {
                if source.is_empty()
                    || source.starts_with('-')
                    || source.starts_with('.')
                    || source.starts_with('/')
                    || source
                        .strip_prefix("path:")
                        .is_some_and(|p| !p.starts_with('/'))
                {
                    bail!(
                        "invalid Nix flake reference '{source}'; use a registry name, URL, or path:/absolute/path"
                    );
                }
                (source, attribute, true)
            }
            None => ("nixpkgs", name, false),

View on GitHub (pinned to 533346cc37)