{"record":{"id":"63d2d39efe0d045d","repo":"jdx/mise","slug":"service-unit-name-must-set-a-non-empty-exec-s","errorCode":null,"errorMessage":"service unit '{name}' must set a non-empty `exec_start`","messagePattern":"service unit '(.+?)' must set a non-empty `exec_start`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/systemd.rs","lineNumber":176,"sourceCode":"        if !valid_name(&name) {\n            bail!(\"unit name '{name}' must contain only letters, numbers, '.', '_', '-', or '@'\");\n        }\n        let is_timer = config.on_boot_sec.is_some()\n            || config.on_unit_active_sec.is_some()\n            || config.on_unit_inactive_sec.is_some()\n            || config.on_calendar.is_some()\n            || config.randomized_delay_sec.is_some()\n            || config.accuracy_sec.is_some()\n            || config.persistent.is_some()\n            || config.unit.is_some();\n        let kind = if is_timer {\n            SystemdUnitKind::Timer\n        } else {\n            SystemdUnitKind::Service\n        };\n        let exec_start = config.exec_start.map(|s| s.trim().to_string());\n        if kind == SystemdUnitKind::Service && exec_start.as_deref().is_none_or(str::is_empty) {\n            bail!(\"service unit '{name}' must set a non-empty `exec_start`\");\n        }\n        if kind == SystemdUnitKind::Timer {\n            let service_only_fields = [\n                (exec_start.is_some(), \"exec_start\"),\n                (config.service_type.is_some(), \"type\"),\n                (config.remain_after_exit.is_some(), \"remain_after_exit\"),\n                (config.exec_stop.is_some(), \"exec_stop\"),\n                (config.timeout_start_sec.is_some(), \"timeout_start_sec\"),\n                (config.timeout_stop_sec.is_some(), \"timeout_stop_sec\"),\n                (config.no_new_privileges.is_some(), \"no_new_privileges\"),\n                (config.private_tmp.is_some(), \"private_tmp\"),\n                (!config.environment.is_empty(), \"environment\"),\n                (!config.environment_file.is_empty(), \"environment_file\"),\n                (config.nice.is_some(), \"nice\"),\n                (config.umask.is_some(), \"umask\"),\n                (config.working_directory.is_some(), \"working_directory\"),\n                (config.restart.is_some(), \"restart\"),\n                (config.restart_sec.is_some(), \"restart_sec\"),","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/systemd.rs#L158-L194","documentation":"A systemd *service* unit must define what to run. `SystemdRequest::from_toml` requires a non-empty `exec_start` (after trimming whitespace) whenever the unit resolves to a Service. Timers are exempt because they reference a companion service instead.","triggerScenarios":"Declaring `[systemd.<name>]` with no `exec_start` key, or `exec_start = \"\"` or whitespace-only, on a unit that has no timer trigger fields (so kind == Service).","commonSituations":"Copy-pasting a timer config and forgetting `exec_start`; commenting out the exec line while debugging; defining a unit intended only to be triggered by an existing timer but configuring it as a plain service.","solutions":["Add a non-empty `exec_start` value, e.g. `exec_start = \"/usr/bin/myapp --serve\"`.","If the unit is meant to be timer-driven, add a timer trigger field (`on_boot_sec`, `on_calendar`, etc.) so it is treated as a Timer and define the schedule there.","Remove the unit entirely if it was added by mistake (`state = \"absent\"` semantics apply at the service level, not here)."],"exampleFix":"# before\n[systemd.myapp]\n# exec_start missing\n\n# after\n[systemd.myapp]\nexec_start = \"/usr/local/bin/myapp --daemon\"","handlingStrategy":"validation","validationCode":"fn has_exec_start(cfg: &toml::Value) -> bool {\n    cfg.get(\"exec_start\")\n        .and_then(|v| v.as_str())\n        .map(|s| !s.trim().is_empty())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair a service unit block with an `exec_start` line.","Lint mise.toml systemd blocks in CI before deploy.","When converting a service to a timer, split into two blocks rather than deleting `exec_start`."],"tags":["systemd","validation","missing-field","config"],"backgroundTag":"missing-required-config-field","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}