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

  1. Rename the TOML key to contain only letters, numbers, `.`, `_`, `-`, or `@`.
  2. Remove stray whitespace or path separators from the unit name.
  3. 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

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/8d121fa53f67e499. Report an issue: GitHub.