jdx/mise · error

mode must be between 0000 and 7777

Error message

mode must be between 0000 and 7777

What it means

The mode field of managed files/directories is parsed by parse_mode(): optional "0o" prefix, then strict octal via u32::from_str_radix(_, 8), and the value must be <= 0o7777 (the 12-bit permission mask). Non-octal digits fail earlier with the wrapped "mode must be an octal string" error; this specific bail means the number parsed fine but exceeds 7777.

Source

Thrown at src/system/managed_files.rs:1210

fn validate_privileged_target(path: &Path) -> Result<PathBuf> {
    if !path.is_absolute() {
        bail!("managed system path must be absolute: {}", path.display());
    }
    let path = path.absolutize()?.to_path_buf();
    if path == Path::new("/") {
        bail!("refusing to manage the filesystem root");
    }
    Ok(path)
}

fn parse_mode(mode: Option<&str>, default: u32) -> Result<u32> {
    let Some(mode) = mode else {
        return Ok(default);
    };
    let mode = mode.strip_prefix("0o").unwrap_or(mode);
    let parsed = u32::from_str_radix(mode, 8).wrap_err("mode must be an octal string")?;
    if parsed > 0o7777 {
        bail!("mode must be between 0000 and 7777");
    }
    Ok(parsed)
}

fn nonempty(field: &str, value: Option<String>) -> Result<Option<String>> {
    match value {
        Some(value) if value.trim().is_empty() => bail!("{field} must not be empty"),
        Some(value) => Ok(Some(value)),
        None => Ok(None),
    }
}

fn desired_metadata(kind: &str, mode: u32, owner: Option<&str>, group: Option<&str>) -> String {
    let mut desired = format!("{kind} mode {mode:04o}");
    if let Some(owner) = owner {
        desired.push_str(&format!(" owner {owner}"));
    }
    if let Some(group) = group {

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Use a 3-4 digit octal permission string: "0644", "0755", "0600"
  2. If copying from st_mode, keep only the low 12 permission bits

Example fix

# before
[bootstrap.files]
"/etc/app/app.conf" = { content = "...", mode = "100644" }

# after
[bootstrap.files]
"/etc/app/app.conf" = { content = "...", mode = "0644" }
Defensive patterns

Strategy: validation

Validate before calling

let m = mode.strip_prefix("0o").unwrap_or(mode);
let parsed = u32::from_str_radix(m, 8).wrap_err("mode must be an octal string")?;
if parsed > 0o7777 {
    return Err(eyre::eyre!("mode must be between 0000 and 7777"));
}

Type guard

fn is_valid_mode_string(mode: &str) -> bool {
    let m = mode.strip_prefix("0o").unwrap_or(mode);
    !m.is_empty()
        && m.chars().all(|c| ('0'..='7').contains(&c))
        && u32::from_str_radix(m, 8).map(|v| v <= 0o7777).unwrap_or(false)
}

Prevention

When it happens

Trigger: mode = "0o10000" or any octal value above 0o7777; copy-pasting a full st_mode value that includes file-type bits (e.g. "100644" parses as octal but overflows the mask).

Common situations: Pasting stat/st_mode output into config; assuming mode is decimal; adding an extra digit to "0644".

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/e28f064fcfe65906. Report an issue: GitHub.