zeroclaw-labs/zeroclaw · error · anyhow::Error

Declarative cron job '{id}': shell job requires a non-empty

Error message

Declarative cron job '{id}': shell job requires a non-empty 'command'

What it means

validate_decl enforces that declarative cron jobs with job_type = "shell" (the default when job_type is omitted) carry a non-empty command. The command is the entire payload a shell job executes, so a missing or blank value would dispatch a job that does nothing; validation fails during sync_declarative_jobs before any DB write.

Source

Thrown at crates/zeroclaw-runtime/src/cron/store.rs:1404

                    "Inserted declarative cron job from config"
                );
            }
        }

        Ok(())
    })
}

/// Validate a declarative cron job definition.
fn validate_decl(id: &str, decl: &zeroclaw_config::schema::CronJobDecl) -> Result<()> {
    if id.trim().is_empty() {
        anyhow::bail!("Declarative cron job has empty id");
    }

    match decl.job_type.to_lowercase().as_str() {
        "shell" => {
            if decl.command.as_deref().is_none_or(|c| c.trim().is_empty()) {
                anyhow::bail!(
                    "Declarative cron job '{id}': shell job requires a non-empty 'command'"
                );
            }
        }
        "agent" => {
            if decl.prompt.as_deref().is_none_or(|p| p.trim().is_empty()) {
                anyhow::bail!(
                    "Declarative cron job '{id}': agent job requires a non-empty 'prompt'"
                );
            }
            if decl.shell_output_format != zeroclaw_config::schema::CronShellOutputFormat::default()
            {
                anyhow::bail!(
                    "Declarative cron job '{id}': shell_output_format is shell-only and cannot be set on an agent job"
                );
            }
        }
        other => {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Add command = "<your shell command>" to the [cron.<id>] table
  2. If the job should prompt the agent instead, set job_type = "agent" and provide a non-empty prompt
  3. Make sure command is not only whitespace

Example fix

# before
[cron.digest]
schedule = { kind = "cron", expression = "0 8 * * *" }
# job_type defaults to "shell" but command is missing

# after
[cron.digest]
job_type = "shell"
schedule = { kind = "cron", expression = "0 8 * * *" }
command = "./scripts/digest.sh --daily"
Defensive patterns

Strategy: validation

Validate before calling

for (id, decl) in &decls {
    if decl.job_type.eq_ignore_ascii_case("shell")
        && decl.command.as_deref().is_none_or(|c| c.trim().is_empty())
    {
        anyhow::bail!("cron job '{id}': shell job needs a non-empty 'command'");
    }
}

Type guard

fn shell_decl_is_runnable(decl: &zeroclaw_config::schema::CronJobDecl) -> bool {
    !decl.job_type.eq_ignore_ascii_case("shell")
        || decl.command.as_deref().is_some_and(|c| !c.trim().is_empty())
}

Try / catch

if let Err(err) = zeroclaw_runtime::cron::sync_declarative_jobs(&config, &decls) {
    if err.to_string().contains("shell job requires") {
        eprintln!("config error: a shell [cron.<id>] table is missing 'command'");
    }
    return Err(err);
}

Prevention

When it happens

Trigger: A [cron.<id>] table with job_type = "shell" (explicit or by default) and no command key, command = "", or command = " " (whitespace only, trimmed to empty).

Common situations: Writing an agent-style job but forgetting to flip job_type to "agent"; creating a schedule-only stub job to test the scheduler; a YAML/TOML anchor or merge overriding command to empty.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/d48221a3189f2986. Report an issue: GitHub.