tinyhumansai/openhuman · error

At least one of --expression, --tz, --command, or --name mus

Error message

At least one of --expression, --tz, --command, or --name must be provided

What it means

update_cron_job refuses a no-op update: all four optional fields (expression, tz, command, name) are None, so there would be nothing to patch. The guard turns an empty patch into an explicit error instead of a silent non-change.

Source

Thrown at src/openhuman/cron/ops.rs:84

        },
    )
}

/// Update an existing cron job using the same rules as the legacy CLI, but without CLI wiring.
///
/// `expression` and `tz` are merged with the existing [`Schedule::Cron`] fields; the
/// existing `active_hours` is always preserved as-is.  To set or clear `active_hours`
/// directly, use the RPC path (`cron.update` with a full [`CronJobPatch`]).
pub fn update_cron_job(
    config: &Config,
    id: &str,
    expression: Option<String>,
    tz: Option<String>,
    command: Option<String>,
    name: Option<String>,
) -> Result<CronJob> {
    if expression.is_none() && tz.is_none() && command.is_none() && name.is_none() {
        anyhow::bail!("At least one of --expression, --tz, --command, or --name must be provided");
    }

    // Merge expression/tz with the existing schedule so that
    // tz alone updates the timezone and expression alone preserves the timezone.
    let schedule = if expression.is_some() || tz.is_some() {
        let existing = get_job(config, id)?;
        let (existing_expr, existing_tz, existing_active) = match existing.schedule {
            Schedule::Cron {
                expr,
                tz: existing_tz,
                active_hours: existing_active,
            } => (expr, existing_tz, existing_active),
            _ => anyhow::bail!("Cannot update expression/tz on a non-cron schedule"),
        };
        Some(Schedule::Cron {
            expr: expression.unwrap_or(existing_expr),
            tz: tz.or(existing_tz),
            active_hours: existing_active,

View on GitHub (pinned to 7491200858)

Solutions

  1. Pass at least one flag: --expression, --tz, --command, or --name
  2. If the intent was enable/disable, use the RPC path (`cron.update` with a full CronJobPatch carrying `enabled`) rather than this merge helper
  3. In UI/script code, skip the update call when the changed-field set is empty

Example fix

# before
openhuman cron update morning-digest

# after
openhuman cron update morning-digest --name "daily-digest"
Defensive patterns

Strategy: validation

Validate before calling

let changes = [&expression, &tz, &command, &name].into_iter().flatten().count();
if changes == 0 {
    return Ok(job_unchanged); // nothing to do — skip the update call entirely
}

Try / catch

Catch the bail at the ops boundary and translate it into a 4xx 'empty patch' response for RPC callers instead of letting it surface as a store-level failure.

Prevention

When it happens

Trigger: CLI `openhuman cron update <id>` invoked without any of --expression/--tz/--command/--name; RPC callers building a partial update where every field is None; UI forms submitting although no field changed.

Common situations: A Save button that always calls update even with no diff; option parsers mapping missing flags to None; scripting loops over a diff set that turns out empty.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/762eb93ea294e73d. Report an issue: GitHub.