{"record":{"id":"12942f23f260c47a","repo":"chenhg5/cc-connect","slug":"either-prompt-or-exec-is-required-12942f","errorCode":null,"errorMessage":"either prompt or exec is required","messagePattern":"either prompt or exec is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"core/api.go","lineNumber":351,"sourceCode":"\t\thttp.Error(w, \"POST only\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\tif s.cron == nil {\n\t\thttp.Error(w, \"cron scheduler not available\", http.StatusServiceUnavailable)\n\t\treturn\n\t}\n\n\tvar req CronAddRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"invalid JSON: \"+err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.CronExpr == \"\" {\n\t\thttp.Error(w, \"cron_expr is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.Prompt == \"\" && req.Exec == \"\" {\n\t\thttp.Error(w, \"either prompt or exec is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.Prompt != \"\" && req.Exec != \"\" {\n\t\thttp.Error(w, \"prompt and exec are mutually exclusive\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Resolve project: use provided, or pick single engine\n\tproject := req.Project\n\tif project == \"\" {\n\t\ts.mu.RLock()\n\t\tif len(s.engines) == 1 {\n\t\t\tfor name := range s.engines {\n\t\t\t\tproject = name\n\t\t\t}\n\t\t}\n\t\ts.mu.RUnlock()\n\t}","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/api.go#L333-L369","documentation":"handleCronAdd validates cron-job creation requests in the HTTP API. A cron job must be driven by either a natural-language prompt or an exec command; the server rejects requests that carry neither, preventing creation of a job that would do nothing when fired.","triggerScenarios":"POST to the cron-add endpoint where req.Prompt == \"\" and req.Exec == \"\" (both omitted or empty strings) while cron_expr is present.","commonSituations":"Clients building the JSON body dynamically omit both fields when a variable is empty; tooling sends only {\"cron_expr\": \"0 9 * * *\"}; copy-pasted curl examples drop the prompt field.","solutions":["Add a non-empty \"prompt\" (for agent-driven jobs) OR \"exec\" (for shell commands) to the request JSON","Check the client code that builds the body — an empty variable is likely being serialized as \"\" and omitted downstream","Confirm you are not sending both later; note prompt and exec are mutually exclusive (next validation)","Inspect the response of a prior step that was supposed to supply the prompt/exec value"],"exampleFix":"// before\n{\"cron_expr\": \"0 9 * * *\"}\n// after\n{\"cron_expr\": \"0 9 * * *\", \"prompt\": \"summarize open issues\"}","handlingStrategy":"validation","validationCode":"const body = { cron_expr: expr, prompt, exec };\nif (!body.prompt && !body.exec) throw new Error('cron job needs a prompt or exec');\nif (body.prompt && body.exec) throw new Error('prompt and exec are mutually exclusive');","typeGuard":"function hasCronAction(b) { return (typeof b?.prompt === 'string' && b.prompt.length > 0) || (typeof b?.exec === 'string' && b.exec.length > 0); }","tryCatchPattern":null,"preventionTips":["Validate the request body client-side before POSTing","Never build the body from possibly-empty variables without a default","Remember prompt and exec are mutually exclusive — enforce that in your client model"],"tags":["http-api","validation","cron"],"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-14T00:17:10.932Z"}