jdx/mise · error

timer unit ' ' cannot set service-only directive(s)

Error message

timer unit '{name}' cannot set service-only directive(s): {}

What it means

When from_toml classifies a unit as a Timer (because a scheduling key is present), service-only directives such as exec_start, type, remain_after_exit, standard_output/standard_error, etc. are not allowed on it. The error lists exactly which offending fields were set, since systemd defines those on the paired service unit, not the timer.

Solutions

  1. Remove the listed service-only directives from the timer entry — mise generates the paired service unit itself
  2. If you need exec_start and its options, split into two entries: a timer entry (schedule only) and a service entry (exec_start etc.)
  3. Check each named field in the error message and delete it from the timer's TOML table

Example fix

# before (mise.toml)
[systemd."backup.timer"]
on_calendar = "daily"
exec_start = "/usr/bin/backup"
# after
[systemd."backup.timer"]
on_calendar = "daily"
Defensive patterns

Strategy: validation

Validate before calling

const SERVICE_ONLY: &[&str] = &["exec_start","type","remain_after_exit","standard_output","standard_error"];
// reject timer entries that set any of these

Prevention

When it happens

Trigger: Defining a timer unit in mise.toml that sets both a schedule key (on_boot_sec / on_unit_active_sec / on_unit_inactive_sec / on_calendar / randomized_delay_sec) and one or more service-only fields (exec_start, type, remain_after_exit, standard_output, standard_error, ...).

Common situations: Copy-pasting a service block and adding on_calendar to it; misunderstanding that mise generates the timer/service pair automatically; trying to configure the service's stdout through the timer entry.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at src/system/systemd.rs:202

                (config.timeout_start_sec.is_some(), "timeout_start_sec"),
                (config.timeout_stop_sec.is_some(), "timeout_stop_sec"),
                (config.no_new_privileges.is_some(), "no_new_privileges"),
                (config.private_tmp.is_some(), "private_tmp"),
                (!config.environment.is_empty(), "environment"),
                (!config.environment_file.is_empty(), "environment_file"),
                (config.nice.is_some(), "nice"),
                (config.umask.is_some(), "umask"),
                (config.working_directory.is_some(), "working_directory"),
                (config.restart.is_some(), "restart"),
                (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)
        {

View on GitHub (pinned to 533346cc37)