jdx/mise · error

agent ' ' must set `program

Error message

agent '{name}' must set `program`

What it means

LaunchdRequest::from_toml requires every launchd agent to define a `program` field, since launchd needs a command to run. If LaunchdTomlConfig.program is None (missing from the TOML), the function bails with this message. It is a missing-required-field check at config parse time.

Solutions

  1. Add a `program` key to the agent config pointing at the executable to run
  2. Remove the agent section if it is not needed
  3. Verify the TOML section is not shadowed so `program` actually lands in the intended agent

Example fix

# before
[agent.backup]
keep_alive = true
# after
[agent.backup]
program = "/usr/local/bin/backup.sh"
keep_alive = true
Defensive patterns

Strategy: validation

Validate before calling

if "program" not in agent_cfg:
    raise ValueError(f"agent '{name}' needs a program")

Prevention

When it happens

Trigger: Defining an [agent.NAME] section without a `program` key, or programmatically constructing LaunchdTomlConfig with program = None.

Common situations: Creating an empty agent stub and forgetting to fill in program; commenting out the program line while keeping the agent section; copying an agent config that relies on a key another version supported.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at src/system/launchd.rs:115

    Differs,
    Missing,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct LaunchdStatus {
    pub request: LaunchdRequest,
    pub path: PathBuf,
    pub loaded: bool,
    pub state: LaunchdState,
}

impl LaunchdRequest {
    pub(crate) fn from_toml(name: String, config: LaunchdTomlConfig) -> Result<Self> {
        if !valid_name(&name) {
            bail!("agent name '{name}' must contain only letters, numbers, '.', '_', or '-'");
        }
        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

View on GitHub (pinned to 533346cc37)