{"record":{"id":"88aa8e0bb53027a0","repo":"zeroclaw-labs/zeroclaw","slug":"cannot-update-expression-tz-on-a-non-cron-schedule","errorCode":null,"errorMessage":"Cannot update expression/tz on a non-cron schedule","messagePattern":"Cannot update expression/tz on a non-cron schedule","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/cron/mod.rs","lineNumber":584,"sourceCode":"                validate_delivery_config(merged.as_ref())?;\n                merged\n            } else {\n                None\n            };\n\n            // Merge expression/tz with the existing schedule so that\n            // --tz alone updates the timezone and --expression alone\n            // preserves the existing timezone.\n            let schedule = if expression.is_some() || tz.is_some() {\n                let existing = existing\n                    .as_ref()\n                    .expect(\"existing job must be loaded when updating schedule\");\n                let (existing_expr, existing_tz) = match &existing.schedule {\n                    Schedule::Cron {\n                        expr,\n                        tz: existing_tz,\n                    } => (expr.clone(), existing_tz.clone()),\n                    _ => bail!(\"Cannot update expression/tz on a non-cron schedule\"),\n                };\n                Some(Schedule::Cron {\n                    expr: expression.unwrap_or(existing_expr),\n                    tz: tz.or(existing_tz),\n                })\n            } else {\n                None\n            };\n\n            if !allowed_tools.is_empty() {\n                let existing = existing\n                    .as_ref()\n                    .expect(\"existing job must be loaded when updating allowed tools\");\n                if existing.job_type != JobType::Agent {\n                    bail!(\"--allowed-tool is only supported for agent cron jobs\");\n                }\n            }\n","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/src/cron/mod.rs#L566-L602","documentation":"When `cron update` receives --expression or --tz, it loads the existing job to merge the new schedule. Those flags only make sense for jobs whose schedule is Schedule::Cron; if the stored job's schedule is a different variant (Schedule::At one-shot or Schedule::Every interval), the merge is refused with this error and the job is left untouched.","triggerScenarios":"`zeroclaw cron update <id> --expression '0 * * * *'` or `--tz Europe/Berlin` on a job originally created via cron add-at, add-every, or once (delay one-shot) rather than the expression-based add.","commonSituations":"Users forgetting which job flavor an id refers to; scripts blindly patching --expression/--tz across every job id; trying to convert a one-shot to recurring by updating its expression.","solutions":["Check the job's schedule type first (cron list shows schedule details) and only send schedule flags that match it.","To convert a one-shot or interval job into a cron-expression job, delete it and re-create it with `cron add --expression ...` (ids do not transfer).","For interval jobs, update the interval field through its own flag rather than --expression."],"exampleFix":"# before: patching expression on a one-shot job\nzeroclaw cron update abc123 --expression '0 5 * * *'   # job was created with add-at\n# after: recreate as a cron job\nzeroclaw cron remove abc123\nzeroclaw cron add bot --expression '0 5 * * *' --command 'report.sh'","handlingStrategy":"validation","validationCode":"// before `cron update <id> --expression/--tz`, confirm the job is cron-scheduled\n// `zeroclaw cron list` prints each job's schedule; alternatively read the store:\nfn is_cron_job(schedule_kind: &str) -> bool {\n    schedule_kind == \"cron\" // reject before invoking update otherwise\n}","typeGuard":null,"tryCatchPattern":"match run_cron_update(args).await {\n    Err(e) if e.to_string().contains(\"non-cron schedule\") => {\n        // job is At/Every: recreate it as a cron job instead of patching\n    }\n    other => other,\n}","preventionTips":["Record each job's flavor next to its id in runbooks so schedule updates target the right variant.","In bulk scripts, branch on schedule type before choosing --expression/--tz vs interval/at flags.","Treat type conversion as delete+recreate, never as a patch."],"tags":["cron","cli","schedule","argument-validation","update"],"backgroundTag":"schedule-type-mismatch","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}