jdx/mise · error · eyre::Report

mode must be between 0000 and 7777

Error message

mode must be between 0000 and 7777

What it means

The `mode` field of a managed file/directory must be an octal string no larger than 0o7777 (permission + setuid/setgid/sticky bits). `parse_mode` strips an optional `0o` prefix, parses base-8 (failing earlier with "mode must be an octal string"), and rejects values above 0o7777. Note decimal-looking digits 8/9 are caught by the octal parse, not this message.

Solutions

  1. Correct the mode to a 3-4 digit octal string, e.g. mode = "0644" or mode = "0755".
  2. Drop any bit above 0o7777 — setuid/setgid/sticky are the only extras allowed on top of rwxrwxrwx.
  3. If you meant a decimal value, convert it to octal first (e.g. decimal 493 = octal 755).

Example fix

# before
[bootstrap.files.appconf]
mode = "177770"

# after
[bootstrap.files.appconf]
mode = "0644"
Defensive patterns

Strategy: validation

Validate before calling

import re
def check_mode(m):
    if m is None: return
    s = m.removeprefix('0o')
    if not re.fullmatch(r'[0-7]{3,4}', s) or int(s, 8) > 0o7777:
        raise ValueError(f"mode must be an octal string <= 7777, got {m}")

Prevention

When it happens

Trigger: A [bootstrap.files]/[bootstrap.directories] entry with `mode` whose octal value exceeds 0o7777, e.g. mode = "77770", mode = "0o10000", or a stray extra digit appended to a valid mode.

Common situations: Typo of an extra digit ("07777" vs "7777" then adding another); pasting a full stat output or decimal permission number into mode; confusing decimal 7777 with octal 0o7777.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/system/managed_files.rs:1387

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 533346cc37)