jdx/mise · error

timer unit ' ' must set at least one of `on_boot_sec`…

Error message

timer unit '{name}' must set at least one of `on_boot_sec`, `on_unit_active_sec`, `on_unit_inactive_sec`, or `on_calendar`

What it means

A unit classified as a Timer must carry at least one scheduling directive: on_boot_sec, on_unit_active_sec, on_unit_inactive_sec, or on_calendar. from_toml rejects timer entries with none of these because systemd would create a timer that never elapses.

Solutions

  1. Add a schedule key, e.g. on_calendar = "daily" or on_boot_sec = 30
  2. If no schedule is wanted, convert the entry to a service by removing timer-ish keys and providing exec_start
  3. Remove the placeholder entry until the schedule is decided

Example fix

# before (mise.toml)
[systemd."cleanup.timer"]
randomized_delay_sec = 60
# after
[systemd."cleanup.timer"]
on_calendar = "daily"
randomized_delay_sec = 60
Defensive patterns

Strategy: validation

Validate before calling

let has_schedule = on_boot_sec.is_some() || on_unit_active_sec.is_some()
    || on_unit_inactive_sec.is_some() || on_calendar.is_some();
if !has_schedule { bail!("timer entry needs on_boot_sec / on_unit_active_sec / on_unit_inactive_sec / on_calendar"); }

Prevention

When it happens

Trigger: Defining a unit in mise.toml whose name/config ends up as a Timer (e.g. randomized_delay_sec present, or intended as a timer) without setting any of the four scheduling keys, then loading it via from_toml.

Common situations: Scaffolding a timer entry and planning to fill in the schedule later; accidentally deleting the on_calendar line; setting only randomized_delay_sec believing it schedules the unit (it only jitters an existing schedule).

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/17fe54615ee8cf4e. Report an issue: GitHub.

Appendix: source

Thrown at src/system/systemd.rs:212

                (config.restart_sec.is_some(), "restart_sec"),
                (config.standard_output.is_some(), "standard_output"),
                (config.standard_error.is_some(), "standard_error"),
            ]
            .into_iter()
            .filter_map(|(is_set, field)| is_set.then_some(field))
            .collect::<Vec<_>>();
            if !service_only_fields.is_empty() {
                bail!(
                    "timer unit '{name}' cannot set service-only directive(s): {}",
                    service_only_fields.join(", ")
                );
            }
            if config.on_boot_sec.is_none()
                && config.on_unit_active_sec.is_none()
                && config.on_unit_inactive_sec.is_none()
                && config.on_calendar.is_none()
            {
                bail!(
                    "timer unit '{name}' must set at least one of `on_boot_sec`, \
                     `on_unit_active_sec`, `on_unit_inactive_sec`, or `on_calendar`"
                );
            }
        }
        if let Some(nice) = config.nice
            && !(-20..=19).contains(&nice)
        {
            bail!("service unit '{name}' has invalid `nice` value {nice}; expected -20 through 19");
        }
        if let Some(umask) = &config.umask
            && !valid_umask(umask)
        {
            bail!(
                "service unit '{name}' has invalid `umask` value '{umask}'; expected an octal access mask from 0000 through 0777"
            );
        }
        let wanted_by = config.wanted_by.unwrap_or_else(|| match kind {

View on GitHub (pinned to 533346cc37)