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

Declarative cron job has empty id

Error message

Declarative cron job has empty id

What it means

Thrown by validate_decl while sync_declarative_jobs copies declarative cron jobs from config into the SQLite cron store. The map key (a [cron.<id>] table key in zeroclaw.toml) becomes the job's permanent id used by the scheduler, the cron_jobs.id column, and CLI/API addressing, so an id that is empty or only whitespace cannot be addressed and validation aborts before any DB write. Validation runs for every entry before the sync touches the database.

Source

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

                })?;

                ::zeroclaw_log::record!(
                    INFO,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                        .with_attrs(::serde_json::json!({"job_id": id})),
                    "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()
            {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Open zeroclaw.toml and give the offending [cron.<id>] table a non-empty, unique id such as [cron.daily-backup]; search for [cron.""] or whitespace-only keys
  2. If config is generated, assert every emitted id is non-empty before writing the file
  3. Restart the daemon or re-run zeroclaw cron list to confirm the job now syncs

Example fix

# zeroclaw.toml — before
[cron.""]
job_type = "shell"
schedule = { kind = "cron", expression = "0 3 * * *" }
command = "backup.sh"

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

Strategy: validation

Validate before calling

// run before syncing declarative cron jobs into the store
for id in decls.keys() {
    if id.trim().is_empty() {
        anyhow::bail!("zeroclaw.toml: a [cron.<id>] table key is empty or whitespace");
    }
}
zeroclaw_runtime::cron::sync_declarative_jobs(&config, &decls)?;

Type guard

fn cron_ids_are_addressable(decls: &std::collections::HashMap<String, zeroclaw_config::schema::CronJobDecl>) -> bool {
    decls.keys().all(|id| !id.trim().is_empty())
}

Try / catch

match zeroclaw_runtime::cron::sync_declarative_jobs(&config, &decls) {
    Err(err) if err.to_string().contains("empty id") => {
        eprintln!("config error: a [cron.<id>] table has a blank id — fix zeroclaw.toml");
        std::process::exit(2);
    }
    other => other?,
}

Prevention

When it happens

Trigger: Daemon or scheduler startup (or the gateway API path) calls sync_declarative_jobs with a decls map containing a key of "" or only whitespace. In TOML this is a table like [cron.""] or [cron." "]; programmatically it is config.cron.insert(String::new(), decl) or a config generator emitting a blank id.

Common situations: Hand-editing zeroclaw.toml and leaving an empty table header after renaming a job; templating that renders an unset variable as the id (for example [cron."${JOB_NAME}"]); copy-pasting a job block and deleting the id line.

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/361480125de40509. Report an issue: GitHub.