# CronJob Never Scheduled

> A CronJob with no lastScheduleTime has never run since it was created. Unlike a suspended job this one is active and simply never triggered, usually a cron expression that is valid syntax but describes a moment that has not arrived, such as day 31 in a 30-day month.

Source: https://zop.dev/integrations/kubernetes/recommendations/cronjob-never-scheduled
Updated: 2026-08-19

---

## Active, enabled, and still never fired

This is not the suspended case. The CronJob is enabled, the controller is watching it, and
`status.lastScheduleTime` is empty. Kubernetes considers everything healthy.

The schedule is syntactically valid (an invalid one would be rejected at admission), but it
describes a time that has not occurred since the object was created.

## The cron expressions that do this

**Impossible or near-impossible dates.** `0 0 31 2 *` is the 31st of February, which never
arrives. `0 0 30 * *` skips February entirely and fires eleven times a year.

**Timezone assumptions.** Kubernetes CronJob schedules evaluate in UTC unless the cluster runs a
version supporting `spec.timeZone` and it is set. A job written for 2am local time may be firing
at a very different hour, or a maintenance window may have been missed entirely.

**Day-of-month and day-of-week together.** In cron these are ORed, not ANDed. `0 0 1 * 1` means
"the 1st of the month **or** any Monday", which is almost never what the author intended, though
it fires often rather than never.

**A far-future first occurrence.** An annual job created in March with a January schedule
legitimately shows nothing for ten months, and is not a defect.

## Distinguishing broken from simply not due yet

Compare the creation timestamp against the schedule's natural period. A daily job created a week
ago with no run is broken. An annual job created last month is fine.

That check is why the rule surfaces this for review rather than asserting a fault: it cannot
know your intended cadence.

## Finding CronJobs with a null lastScheduleTime

```bash
kubectl get cronjob -A -o json | jq -r '
  .items[] | select(.status.lastScheduleTime == null and (.spec.suspend != true))
  | "\(.metadata.namespace)/\(.metadata.name)\t\(.spec.schedule)\tcreated=\(.metadata.creationTimestamp)"'
```

Read the schedule column against the creation date. That pairing answers it faster than
anything else.
