{"record":{"id":"8d121fa53f67e499","repo":"jdx/mise","slug":"unit-name-name-must-contain-only-letters-numb","errorCode":null,"errorMessage":"unit name '{name}' must contain only letters, numbers, '.', '_', '-', or '@'","messagePattern":"unit name '(.+?)' must contain only letters, numbers, '\\.', '_', '-', or '@'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/systemd.rs","lineNumber":159,"sourceCode":"    pub active: bool,\n    pub enabled: bool,\n    pub state: SystemdState,\n}\n\nimpl SystemdStatus {\n    pub(crate) fn is_desired(&self) -> bool {\n        match self.state {\n            SystemdState::Active => self.request.start,\n            SystemdState::Inactive => !self.request.start,\n            SystemdState::Differs | SystemdState::Missing => false,\n        }\n    }\n}\n\nimpl SystemdRequest {\n    pub(crate) fn from_toml(name: String, config: SystemdTomlConfig) -> Result<Self> {\n        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        }","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/systemd.rs#L141-L177","documentation":"SystemdRequest::from_toml validates unit names from mise.toml systemd config against valid_name(), which permits only letters, numbers, '.', '_', '-', and '@'. A name containing any other character (spaces, slashes, colons, etc.) is rejected before a unit file is generated, preventing malformed or unsafe systemd unit names.","triggerScenarios":"Defining a systemd unit in mise.toml ([systemd] or equivalent table) whose key/name contains characters outside [A-Za-z0-9._@-], then running the command that loads systemd requests via from_toml.","commonSituations":"Copy-pasting a unit description with spaces into the name field; using a path-like name such as 'my/app.service'; typos like 'worker:one.service'; shell-interpolated names.","solutions":["Rename the unit to only use letters, numbers, '.', '_', '-', '@' (e.g. 'my-worker.service')","Move descriptive text into config fields like description/comment, not the unit name","Check for accidental whitespace or trailing characters in the TOML key"],"exampleFix":"# before (mise.toml)\n[systemd.\"my app.service\"]\nexec_start = \"/usr/bin/myapp\"\n# after\n[systemd.\"my-app.service\"]\nexec_start = \"/usr/bin/myapp\"","handlingStrategy":"validation","validationCode":"fn valid_unit_name(name: &str) -> bool {\n    !name.is_empty()\n        && name.chars().all(|c| c.is_ascii_alphanumeric() || \"._-@\".contains(c))\n}\n// check before writing the TOML entry","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use kebab-case or dot-separated names for systemd units (e.g. my-app.service)","Never put spaces, slashes, or colons in unit names","Put human-readable descriptions in dedicated fields, not the unit key"],"tags":["systemd","validation","naming","config"],"backgroundTag":"invalid-identifier-format","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}