{"record":{"id":"7bf6a15fe0ff56ff","repo":"chenhg5/cc-connect","slug":"timeout-mins-must-be-0","errorCode":null,"errorMessage":"timeout_mins must be >= 0","messagePattern":"timeout_mins must be >= 0","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"core/cron.go","lineNumber":111,"sourceCode":"\t// `platform \"\" not found for session \"\"`. Reject it up front so the\n\t// caller (management API, /cron/add, /cron edit) sees an immediate\n\t// 400 instead of a job that silently never runs.\n\tif strings.TrimSpace(j.SessionKey) == \"\" {\n\t\treturn fmt.Errorf(\"session_key is required\")\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 (want default, bypassPermissions, acceptEdits, plan, auto, or dontAsk)\", 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}\n\treturn nil\n}\n\n// CronStore persists cron jobs to a JSON file.\ntype CronStore struct {\n\tpath string\n\tmu   sync.Mutex\n\tjobs []*CronJob\n}\n\nfunc NewCronStore(dataDir string) (*CronStore, error) {\n\tdir := filepath.Join(dataDir, \"crons\")\n\tif err := os.MkdirAll(dir, 0o755); err != nil {\n\t\treturn nil, err\n\t}\n\tpath := filepath.Join(dir, \"jobs.json\")\n\ts := &CronStore{path: path}","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/cron.go#L93-L129","documentation":"validateCronJob rejects a CronJob whose TimeoutMins pointer is set to a negative value. Cron job timeouts must be zero or positive minutes; a negative value cannot be honored by the scheduler and is caught before the job is persisted or scheduled. AddJob runs this validation first, so nothing is stored when it fires.","triggerScenarios":"Calling CronScheduler.AddJob (directly or via handleCronAdd) with a job whose TimeoutMins is a pointer to a negative int, e.g. parsed from `timeout_mins = -5` in a cron add command.","commonSituations":"Users typing negative timeout values in a chat /cron add command; config generators computing timeout as a difference of timestamps that went negative; defaulting logic subtracting elapsed time.","solutions":["Set TimeoutMins to a non-negative value (or leave it nil for no timeout).","Clamp or reject negative input at the command/config layer before constructing the CronJob.","If timeout is computed, take max(0, computed)."],"exampleFix":"// before\njob.TimeoutMins = &(-5)\nsched.AddJob(job)\n// after\nt := 30\njob.TimeoutMins = &t\nif err := sched.AddJob(job); err != nil { return err }","handlingStrategy":"validation","validationCode":"func validTimeout(m *int) bool { return m == nil || *m >= 0 }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp negative computed timeouts to 0 before constructing the job.","Validate user-supplied numbers at the command layer.","Leave TimeoutMins nil when no timeout is intended."],"tags":["go","cron","validation"],"backgroundTag":"value-out-of-range","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"}