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
- 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
- If config is generated, assert every emitted id is non-empty before writing the file
- 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
- Lint generated config: assert cron ids are non-empty and unique before writing the file
- Use descriptive kebab-case ids (daily-backup) so blank or placeholder keys stand out in review
- Run the daemon's config validation after every zeroclaw.toml edit instead of discovering errors at startup
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
- Declarative cron job '{id}': shell job requires a non-empty
- Declarative cron job '{id}': agent job requires a non-empty
- Declarative cron job '{id}': shell_output_format is shell-on
- Declarative cron job '{id}': invalid job_type '{other}', exp
- ACP request_permission failed: {} ({})
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/361480125de40509.
Report an issue: GitHub.