chenhg5/cc-connect · error
cron job not found
Error message
cron job not found
What it means
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.
Source
Thrown at core/cron.go:51
Silent *bool `json:"silent,omitempty"` // suppress start notification; nil = use global default
Mute bool `json:"mute,omitempty"` // suppress ALL messages (start + result); job runs silently
SessionMode string `json:"session_mode,omitempty"` // "" or "reuse" = share active session; "new_per_run" = fresh session each run
Mode string `json:"mode,omitempty"` // permission mode override for this job; "" = use project default
TimeoutMins *int `json:"timeout_mins,omitempty"` // nil = default 30m wait; 0 = no limit; >0 = minutes
CreatedAt time.Time `json:"created_at"`
LastRun time.Time `json:"last_run,omitempty"`
LastError string `json:"last_error,omitempty"`
}
// IsShellJob returns true if the job runs a shell command directly.
func (j *CronJob) IsShellJob() bool {
return j.Exec != ""
}
const defaultCronJobTimeout = 30 * time.Minute
var (
ErrCronJobNotFound = errors.New("cron job not found")
ErrCronProjectNotFound = errors.New("cron project not found")
)
// ExecutionTimeout returns how long the scheduler waits for the job goroutine to finish.
// nil TimeoutMins uses 30 minutes. *TimeoutMins == 0 means wait without a time limit.
// *TimeoutMins > 0 means that many minutes.
func (j *CronJob) ExecutionTimeout() time.Duration {
if j.TimeoutMins == nil {
return defaultCronJobTimeout
}
if *j.TimeoutMins <= 0 {
return 0
}
return time.Duration(*j.TimeoutMins) * time.Minute
}
// UsesNewSessionPerRun reports whether each cron run should use a new engine session
// instead of reusing the active session for the session_key.View on GitHub (pinned to 4000b2338a)
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
Example fix
// before
if err := s.cron.RunJobNow(id); err != nil { return err }
// after
if err := s.cron.RunJobNow(id); err != nil {
if errors.Is(err, ErrCronJobNotFound) {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
if s.cron.Store().Get(jobID) == nil {
return fmt.Errorf("job %q does not exist", jobID)
} Type guard
func isCronJobNotFound(err error) bool { return errors.Is(err, ErrCronJobNotFound) } Try / catch
if err := s.cron.RunJobNow(id); err != nil {
if errors.Is(err, ErrCronJobNotFound) {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- cron project not found
- session_key is required
- invalid session_mode %q (want reuse, new_per_run, or new-per
- job %q not found
- ErrCronJobNotFound
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/10bc5e36d7dd1e2c.
Report an issue: GitHub.