{"record":{"id":"d48221a3189f2986","repo":"zeroclaw-labs/zeroclaw","slug":"declarative-cron-job-id-shell-job-requires-a","errorCode":null,"errorMessage":"Declarative cron job '{id}': shell job requires a non-empty 'command'","messagePattern":"Declarative cron job '(.+?)': shell job requires a non-empty 'command'","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/cron/store.rs","lineNumber":1404,"sourceCode":"                    \"Inserted declarative cron job from config\"\n                );\n            }\n        }\n\n        Ok(())\n    })\n}\n\n/// Validate a declarative cron job definition.\nfn validate_decl(id: &str, decl: &zeroclaw_config::schema::CronJobDecl) -> Result<()> {\n    if id.trim().is_empty() {\n        anyhow::bail!(\"Declarative cron job has empty id\");\n    }\n\n    match decl.job_type.to_lowercase().as_str() {\n        \"shell\" => {\n            if decl.command.as_deref().is_none_or(|c| c.trim().is_empty()) {\n                anyhow::bail!(\n                    \"Declarative cron job '{id}': shell job requires a non-empty 'command'\"\n                );\n            }\n        }\n        \"agent\" => {\n            if decl.prompt.as_deref().is_none_or(|p| p.trim().is_empty()) {\n                anyhow::bail!(\n                    \"Declarative cron job '{id}': agent job requires a non-empty 'prompt'\"\n                );\n            }\n            if decl.shell_output_format != zeroclaw_config::schema::CronShellOutputFormat::default()\n            {\n                anyhow::bail!(\n                    \"Declarative cron job '{id}': shell_output_format is shell-only and cannot be set on an agent job\"\n                );\n            }\n        }\n        other => {","sourceCodeStart":1386,"sourceCodeEnd":1422,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/cron/store.rs#L1386-L1422","documentation":"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.","triggerScenarios":"A [cron.<id>] table with job_type = \"shell\" (explicit or by default) and no command key, command = \"\", or command = \"   \" (whitespace only, trimmed to empty).","commonSituations":"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.","solutions":["Add command = \"<your shell command>\" to the [cron.<id>] table","If the job should prompt the agent instead, set job_type = \"agent\" and provide a non-empty prompt","Make sure command is not only whitespace"],"exampleFix":"# before\n[cron.digest]\nschedule = { kind = \"cron\", expression = \"0 8 * * *\" }\n# job_type defaults to \"shell\" but command is missing\n\n# after\n[cron.digest]\njob_type = \"shell\"\nschedule = { kind = \"cron\", expression = \"0 8 * * *\" }\ncommand = \"./scripts/digest.sh --daily\"","handlingStrategy":"validation","validationCode":"for (id, decl) in &decls {\n    if decl.job_type.eq_ignore_ascii_case(\"shell\")\n        && decl.command.as_deref().is_none_or(|c| c.trim().is_empty())\n    {\n        anyhow::bail!(\"cron job '{id}': shell job needs a non-empty 'command'\");\n    }\n}","typeGuard":"fn shell_decl_is_runnable(decl: &zeroclaw_config::schema::CronJobDecl) -> bool {\n    !decl.job_type.eq_ignore_ascii_case(\"shell\")\n        || decl.command.as_deref().is_some_and(|c| !c.trim().is_empty())\n}","tryCatchPattern":"if let Err(err) = zeroclaw_runtime::cron::sync_declarative_jobs(&config, &decls) {\n    if err.to_string().contains(\"shell job requires\") {\n        eprintln!(\"config error: a shell [cron.<id>] table is missing 'command'\");\n    }\n    return Err(err);\n}","preventionTips":["Decide the job type first, then fill exactly its required field: shell needs command, agent needs prompt","When converting a job between types, delete the other type's fields in the same edit","Keep one example of each job type in config comments as a template"],"tags":["cron","config-validation","shell"],"backgroundTag":"config-validation-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}