jdx/mise · error
unit name '{name}' must contain only letters, numbers, '.',
Error message
unit name '{name}' must contain only letters, numbers, '.', '_', '-', or '@' What it means
Systemd unit names are validated at parse time in `SystemdRequest::from_toml`. A name containing characters outside the allowed set (letters, numbers, `.`, `_`, `-`, `@`) is rejected because systemd itself would refuse or misinterpret such a unit name. This is a fail-fast schema validation before any systemctl interaction.
Source
Thrown at src/system/systemd.rs:159
pub active: bool,
pub enabled: bool,
pub state: SystemdState,
}
impl SystemdStatus {
pub(crate) fn is_desired(&self) -> bool {
match self.state {
SystemdState::Active => self.request.start,
SystemdState::Inactive => !self.request.start,
SystemdState::Differs | SystemdState::Missing => false,
}
}
}
impl SystemdRequest {
pub(crate) fn from_toml(name: String, config: SystemdTomlConfig) -> Result<Self> {
if !valid_name(&name) {
bail!("unit name '{name}' must contain only letters, numbers, '.', '_', '-', or '@'");
}
let is_timer = config.on_boot_sec.is_some()
|| config.on_unit_active_sec.is_some()
|| config.on_unit_inactive_sec.is_some()
|| config.on_calendar.is_some()
|| config.randomized_delay_sec.is_some()
|| config.accuracy_sec.is_some()
|| config.persistent.is_some()
|| config.unit.is_some();
let kind = if is_timer {
SystemdUnitKind::Timer
} else {
SystemdUnitKind::Service
};
let exec_start = config.exec_start.map(|s| s.trim().to_string());
if kind == SystemdUnitKind::Service && exec_start.as_deref().is_none_or(str::is_empty) {
bail!("service unit '{name}' must set a non-empty `exec_start`");
}View on GitHub (pinned to afd2eddd3a)
Solutions
- Rename the TOML key to contain only letters, numbers, `.`, `_`, `-`, or `@`.
- Remove stray whitespace or path separators from the unit name.
- For templated units, use the `@` form (e.g. `app@.service`) rather than other special characters.
Example fix
# before [systemd."my app:prod"] exec_start = "/usr/bin/app" # after [systemd."my-app-prod.service"] exec_start = "/usr/bin/app"
Defensive patterns
Strategy: validation
Validate before calling
fn valid_unit_name(name: &str) -> bool {
!name.is_empty()
&& name.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-' | '@'))
}
assert!(valid_unit_name("my-app-prod.service")); Prevention
- Keep unit names to `[A-Za-z0-9._@-]` when authoring mise.toml blocks.
- Avoid TOML keys generated from user input without sanitization.
- Use hyphens instead of spaces or colons in environment-suffixed names.
When it happens
Trigger: Defining a `[systemd.<name>]` block in mise.toml whose key contains whitespace, `/`, `:`, or other special characters, which then flows into `SystemdRequest::from_toml` and fails `valid_name`.
Common situations: Typos or pasted names like `my service.service`, `app:prod`, or names with spaces; template placeholders left unexpanded; copied unit names with parameters written incorrectly.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- invalid bootstrap service name '{name}': use at most 255 ASC
- invalid compose project_name '{name}': use lowercase letters
- invalid bootstrap secret name '{name}': use ASCII letters, d
- bootstrap secret '{name}' has invalid environment variable n
- bootstrap service '{name}' cannot be both masked and running
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/8d121fa53f67e499.
Report an issue: GitHub.