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

  1. Re-list cron jobs to get current ids before triggering
  2. Use errors.Is(err, ErrCronJobNotFound) to branch and treat it as 404, not a retryable fault
  3. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/10bc5e36d7dd1e2c. Report an issue: GitHub.