{"record":{"id":"ebc032b1d5e57851","repo":"sipeed/picoclaw","slug":"either-every-or-cron-must-be-specified","errorCode":null,"errorMessage":"either --every or --cron must be specified","messagePattern":"either --every or --cron must be specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/cron/add.go","lineNumber":27,"sourceCode":")\n\nfunc newAddCommand(storePath func() string) *cobra.Command {\n\tvar (\n\t\tname    string\n\t\tmessage string\n\t\tevery   int64\n\t\tcronExp string\n\t\tchannel string\n\t\tto      string\n\t)\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"add\",\n\t\tShort: \"Add a new scheduled job\",\n\t\tArgs:  cobra.NoArgs,\n\t\tRunE: func(cmd *cobra.Command, _ []string) error {\n\t\t\tif every <= 0 && cronExp == \"\" {\n\t\t\t\treturn fmt.Errorf(\"either --every or --cron must be specified\")\n\t\t\t}\n\n\t\t\tvar schedule cron.CronSchedule\n\t\t\tif every > 0 {\n\t\t\t\teveryMS := every * 1000\n\t\t\t\tschedule = cron.CronSchedule{Kind: \"every\", EveryMS: &everyMS}\n\t\t\t} else {\n\t\t\t\tschedule = cron.CronSchedule{Kind: \"cron\", Expr: cronExp}\n\t\t\t}\n\n\t\t\tcs := cron.NewCronService(storePath(), nil)\n\t\t\tjob, err := cs.AddJob(name, schedule, message, channel, to)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error adding job: %w\", err)\n\t\t\t}\n\n\t\t\tfmt.Printf(\"✓ Added job '%s' (%s)\\n\", job.Name, job.ID)\n","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/cron/add.go#L9-L45","documentation":"`picoclaw cron add` needs exactly one scheduling mode: --every N (N in seconds, internally multiplied to EveryMS) or --cron EXPR. RunE rejects the invocation when every <= 0 AND cronExp is empty - that is, neither flag was supplied or --every was 0/negative. No store is touched, so no state changes.","triggerScenarios":"`picoclaw cron add -n name -m msg` with neither --every nor --cron; or only `--every 0` / a negative --every. The check runs before NewCronService/AddJob.","commonSituations":"Scripts that pass `--every $EVERY` with an unset/empty variable (the token disappears); assuming a default schedule exists; flag typos so cobra never binds the value.","solutions":["Add --every N (seconds) for interval jobs, e.g. `--every 300`, or --cron '0 9 * * *' for cron-expression jobs","In scripts, guard the variable: use `--every=${EVERY:?}` or check non-empty before invoking","Run `picoclaw cron add --help` to confirm flag names and aliases (-e, -c)"],"exampleFix":"# before\npicoclaw cron add -n daily-report -m \"summarize inbox\"\n# after\npicoclaw cron add -n daily-report -m \"summarize inbox\" --cron '0 9 * * *'","handlingStrategy":"validation","validationCode":"# in shell scripts, before calling picoclaw cron add\nEVERY=${EVERY:-0}\nif [ \"$EVERY\" -le 0 ] && [ -z \"${CRON:-}\" ]; then\n  echo \"refusing: schedule missing (need --every N>0 or --cron EXPR)\" >&2\n  exit 2\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair `cron add` with exactly one of --every N (N>0) or --cron EXPR","Use `${VAR:?}` or explicit checks for schedule variables in scripts","Remember --every is seconds, not minutes"],"tags":["cron","cli","usage","validation"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}