jdx/mise · error

agent ' ' `queue_directories` must not contain empty entries

Error message

agent '{name}' `queue_directories` must not contain empty entries

What it means

LaunchdRequest::from_toml iterates the agent's queue_directories list and rejects any entry that is empty or whitespace-only after trimming. Empty entries would produce an invalid WatchPaths entry in the generated plist. The check applies to the raw string, independent of platform path rules.

Solutions

  1. Remove empty or whitespace-only entries from queue_directories
  2. Ensure each entry is a real absolute directory path
  3. If building the list programmatically, filter out empty strings before writing config

Example fix

# before
queue_directories = ["/var/spool/mail", ""]
# after
queue_directories = ["/var/spool/mail"]
Defensive patterns

Strategy: validation

Validate before calling

dirs = agent_cfg.get("queue_directories", [])
assert all(d.strip() for d in dirs), "queue_directories contains an empty entry"

Prevention

When it happens

Trigger: queue_directories = ["/var/spool/mail", ""] or [" "] — an array containing an empty or whitespace-only element.

Common situations: Trailing commas or placeholder slots left in the directory list; template expansion producing empty strings; hand-editing the array and leaving a blank entry.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/system/launchd.rs:131

        }
        let Some(program) = config.program.map(|s| s.trim().to_string()) else {
            bail!("agent '{name}' must set `program`");
        };
        if program.is_empty() {
            bail!("agent '{name}' must set a non-empty `program`");
        }
        if config.keep_alive && config.keep_alive_on_failure {
            bail!("agent '{name}' cannot set both `keep_alive` and `keep_alive_on_failure`");
        }
        if config.nice.is_some_and(|nice| !(-20..=20).contains(&nice)) {
            bail!("agent '{name}' `nice` must be between -20 and 20");
        }
        if let Some(interval) = &config.start_calendar_interval {
            interval.validate(&name)?;
        }
        for dir in &config.queue_directories {
            if dir.trim().is_empty() {
                bail!("agent '{name}' `queue_directories` must not contain empty entries");
            }
            // checked against the raw string rather than `Path::is_absolute` on the
            // expanded value: the plist is consumed by macOS launchd, so POSIX rules
            // apply regardless of the platform parsing the config, and on Windows
            // `Path::new("/var/spool").is_absolute()` is false (root, but no prefix)
            if !is_absolute_launchd_path(dir) {
                bail!(
                    "agent '{name}' `queue_directories` entry '{dir}' must be an absolute path \
                     (launchd requires absolute paths; `~` and `~/` are expanded)"
                );
            }
        }
        Ok(Self {
            label: format!("dev.mise.{name}"),
            name,
            program,
            args: config.args,
            run_at_load: config.run_at_load,

View on GitHub (pinned to 533346cc37)