{"record":{"id":"10bc5e36d7dd1e2c","repo":"chenhg5/cc-connect","slug":"cron-job-not-found","errorCode":null,"errorMessage":"cron job not found","messagePattern":"cron job not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/cron.go","lineNumber":51,"sourceCode":"\tSilent      *bool     `json:\"silent,omitempty\"`       // suppress start notification; nil = use global default\n\tMute        bool      `json:\"mute,omitempty\"`         // suppress ALL messages (start + result); job runs silently\n\tSessionMode string    `json:\"session_mode,omitempty\"` // \"\" or \"reuse\" = share active session; \"new_per_run\" = fresh session each run\n\tMode        string    `json:\"mode,omitempty\"`         // permission mode override for this job; \"\" = use project default\n\tTimeoutMins *int      `json:\"timeout_mins,omitempty\"` // nil = default 30m wait; 0 = no limit; >0 = minutes\n\tCreatedAt   time.Time `json:\"created_at\"`\n\tLastRun     time.Time `json:\"last_run,omitempty\"`\n\tLastError   string    `json:\"last_error,omitempty\"`\n}\n\n// IsShellJob returns true if the job runs a shell command directly.\nfunc (j *CronJob) IsShellJob() bool {\n\treturn j.Exec != \"\"\n}\n\nconst defaultCronJobTimeout = 30 * time.Minute\n\nvar (\n\tErrCronJobNotFound     = errors.New(\"cron job not found\")\n\tErrCronProjectNotFound = errors.New(\"cron project not found\")\n)\n\n// ExecutionTimeout returns how long the scheduler waits for the job goroutine to finish.\n// nil TimeoutMins uses 30 minutes. *TimeoutMins == 0 means wait without a time limit.\n// *TimeoutMins > 0 means that many minutes.\nfunc (j *CronJob) ExecutionTimeout() time.Duration {\n\tif j.TimeoutMins == nil {\n\t\treturn defaultCronJobTimeout\n\t}\n\tif *j.TimeoutMins <= 0 {\n\t\treturn 0\n\t}\n\treturn time.Duration(*j.TimeoutMins) * time.Minute\n}\n\n// UsesNewSessionPerRun reports whether each cron run should use a new engine session\n// instead of reusing the active session for the session_key.","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/cron.go#L33-L69","documentation":"ErrCronJobNotFound is the sentinel error returned when a cron operation references a job id that is not in the scheduler's store. RunJobNow and handleCronExec wrap it with the offending id; core/api.go maps it to HTTP 404. It is always safe to test with errors.Is.","triggerScenarios":"Calling scheduler.RunJobNow(id) with an id that was never created, or after the job was deleted; POST/GET to the cron API endpoint with a stale or malformed job id.","commonSituations":"Client kept an id from a previous run after the config was reloaded and jobs recreated with new ids, a race where another user deleted the job between listing and triggering, or a typo'd id in a curl command.","solutions":["Re-list cron jobs to get current ids before triggering","Use errors.Is(err, ErrCronJobNotFound) to branch and treat it as 404, not a retryable fault","If the id came from persisted state, validate it against the store after config reloads"],"exampleFix":"// before\nif err := s.cron.RunJobNow(id); err != nil { return err }\n// after\nif err := s.cron.RunJobNow(id); err != nil {\n    if errors.Is(err, ErrCronJobNotFound) {\n        http.Error(w, err.Error(), http.StatusNotFound)\n        return\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if s.cron.Store().Get(jobID) == nil {\n    return fmt.Errorf(\"job %q does not exist\", jobID)\n}","typeGuard":"func isCronJobNotFound(err error) bool { return errors.Is(err, ErrCronJobNotFound) }","tryCatchPattern":"if err := s.cron.RunJobNow(id); err != nil {\n    if errors.Is(err, ErrCronJobNotFound) {\n        http.Error(w, err.Error(), http.StatusNotFound)\n        return\n    }\n    http.Error(w, err.Error(), http.StatusInternalServerError)\n}","preventionTips":["Always compare with errors.Is, never string matching","Refresh job ids from the list endpoint after config reloads or restarts","Treat 404 as terminal for the request, not retryable"],"tags":["cron","scheduler","not-found","sentinel-error"],"backgroundTag":"record-not-found","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"}