{"record":{"id":"0d9ae9575690e3b3","repo":"chenhg5/cc-connect","slug":"either-prompt-or-exec-is-required","errorCode":null,"errorMessage":"either prompt or exec is required","messagePattern":"either prompt or exec is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/timer.go","lineNumber":68,"sourceCode":"\t\treturn 0\n\t}\n\treturn time.Duration(*j.TimeoutMins) * time.Minute\n}\n\n// UsesNewSessionPerRun reports whether the timer should use a new engine session.\nfunc (j *TimerJob) UsesNewSessionPerRun() bool {\n\treturn NormalizeCronSessionMode(j.SessionMode) == \"new_per_run\"\n}\n\nfunc validateTimerJob(j *TimerJob) error {\n\tif strings.TrimSpace(j.SessionKey) == \"\" {\n\t\treturn fmt.Errorf(\"session_key is required\")\n\t}\n\tif j.ScheduledAt.IsZero() {\n\t\treturn fmt.Errorf(\"scheduled_at is required\")\n\t}\n\tif j.Prompt == \"\" && j.Exec == \"\" {\n\t\treturn fmt.Errorf(\"either prompt or exec is required\")\n\t}\n\tif j.Prompt != \"\" && j.Exec != \"\" {\n\t\treturn fmt.Errorf(\"prompt and exec are mutually exclusive\")\n\t}\n\tmode := NormalizeCronSessionMode(j.SessionMode)\n\tif mode != \"\" && mode != \"new_per_run\" {\n\t\treturn fmt.Errorf(\"invalid session_mode %q (want reuse, new_per_run, or new-per-run)\", j.SessionMode)\n\t}\n\tif j.Mode != \"\" {\n\t\tswitch j.Mode {\n\t\tcase \"default\", \"bypassPermissions\", \"acceptEdits\", \"plan\", \"auto\", \"dontAsk\":\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid mode %q\", j.Mode)\n\t\t}\n\t}\n\tif j.TimeoutMins != nil && *j.TimeoutMins < 0 {\n\t\treturn fmt.Errorf(\"timeout_mins must be >= 0\")\n\t}","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/timer.go#L50-L86","documentation":"validateTimerJob requires every TimerJob to carry work: either a Prompt (text sent to the agent session) or an Exec (shell command). A job with neither would fire and do nothing, so AddJob rejects it.","triggerScenarios":"AddJob with a TimerJob where both Prompt==\"\" and Exec==\"\" — typically a struct built from a config entry containing neither the prompt nor exec key, or code that clears Prompt based on a flag but never sets Exec.","commonSituations":"TOML/JSON timer definitions where the user typed the schedule but forgot the prompt; conditional builders that set Exec only in one branch; copying an existing job and blanking fields.","solutions":["Set Prompt on the job if it should send text to the agent session.","Set Exec instead if the job should run a shell command.","For interactive builders, reject/reprompt the user earlier if neither was supplied.","Add a caller-side check `if j.Prompt == \"\" && j.Exec == \"\"` before AddJob to fail with context."],"exampleFix":"// before\njob := &core.TimerJob{SessionKey: k, ScheduledAt: when} // nothing to do\nsched.AddJob(job)\n// after\njob := &core.TimerJob{SessionKey: k, ScheduledAt: when, Prompt: \"summarize open PRs\"}\nsched.AddJob(job)","handlingStrategy":"validation","validationCode":"if job.Prompt == \"\" && job.Exec == \"\" { return errors.New(\"timer job needs prompt or exec\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make prompt-or-exec a required choice in any job-builder UI","Reject empty prompt/exec at config parse time with the offending job ID","When templating jobs, assert one of the two fields is populated"],"tags":["timer","validation","scheduler"],"backgroundTag":"missing-required-argument","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}