{"record":{"id":"6c2d911fcc340057","repo":"jdx/mise","slug":"agent-name-cannot-set-both-keep-alive-and-k","errorCode":null,"errorMessage":"agent '{name}' cannot set both `keep_alive` and `keep_alive_on_failure`","messagePattern":"agent '(.+?)' cannot set both `keep_alive` and `keep_alive_on_failure`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/launchd.rs","lineNumber":121,"sourceCode":"    pub request: LaunchdRequest,\n    pub path: PathBuf,\n    pub loaded: bool,\n    pub state: LaunchdState,\n}\n\nimpl LaunchdRequest {\n    pub(crate) fn from_toml(name: String, config: LaunchdTomlConfig) -> Result<Self> {\n        if !valid_name(&name) {\n            bail!(\"agent name '{name}' must contain only letters, numbers, '.', '_', or '-'\");\n        }\n        let Some(program) = config.program.map(|s| s.trim().to_string()) else {\n            bail!(\"agent '{name}' must set `program`\");\n        };\n        if program.is_empty() {\n            bail!(\"agent '{name}' must set a non-empty `program`\");\n        }\n        if config.keep_alive && config.keep_alive_on_failure {\n            bail!(\"agent '{name}' cannot set both `keep_alive` and `keep_alive_on_failure`\");\n        }\n        if config.nice.is_some_and(|nice| !(-20..=20).contains(&nice)) {\n            bail!(\"agent '{name}' `nice` must be between -20 and 20\");\n        }\n        if let Some(interval) = &config.start_calendar_interval {\n            interval.validate(&name)?;\n        }\n        for dir in &config.queue_directories {\n            if dir.trim().is_empty() {\n                bail!(\"agent '{name}' `queue_directories` must not contain empty entries\");\n            }\n            // checked against the raw string rather than `Path::is_absolute` on the\n            // expanded value: the plist is consumed by macOS launchd, so POSIX rules\n            // apply regardless of the platform parsing the config, and on Windows\n            // `Path::new(\"/var/spool\").is_absolute()` is false (root, but no prefix)\n            if !is_absolute_launchd_path(dir) {\n                bail!(\n                    \"agent '{name}' `queue_directories` entry '{dir}' must be an absolute path \\","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/launchd.rs#L103-L139","documentation":"launchd agents support either keep_alive (restart always) or keep_alive_on_failure (restart only on non-zero exit), but not both simultaneously, since their semantics conflict. from_toml enforces this mutual exclusion when parsing the agent config. The user must pick one keep-alive strategy.","triggerScenarios":"Setting both keep_alive = true and keep_alive_on_failure = true on the same [launchd.<name>] agent section.","commonSituations":"Merging two agent configs (one using each option) during refactoring; copy-pasting keep_alive from an example while also enabling keep_alive_on_failure for crash-only restarts; experimenting with both restart modes.","solutions":["Keep only keep_alive = true if the agent should always be running","Keep only keep_alive_on_failure = true if the agent should restart solely after failures","Remove the boolean you do not want from the agent's TOML section"],"exampleFix":"// before\n[launchd.my-agent]\nkeep_alive = true\nkeep_alive_on_failure = true\n// after\n[launchd.my-agent]\nkeep_alive_on_failure = true","handlingStrategy":"validation","validationCode":"let keep_alive = cfg.get(\"keep_alive\").and_then(|v| v.as_bool()).unwrap_or(false);\nlet on_failure = cfg.get(\"keep_alive_on_failure\").and_then(|v| v.as_bool()).unwrap_or(false);\nif keep_alive && on_failure {\n    anyhow::bail!(\"choose either keep_alive or keep_alive_on_failure, not both\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one restart policy per agent and document it","When merging configs, drop one of the two keys explicitly","Default to keep_alive_on_failure for crash-only services"],"tags":["validation","configuration","launchd"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}