go-cron API Reference
June 2, 2026 · View on GitHub
Complete API documentation for all exported types, functions, and methods
Table of Contents
- Package cron
- Types
- Cron
- Entry
- EntryID
- Option
- JobOption
- Schedule
- ScheduleWithPrev
- SpecSchedule
- ConstantDelaySchedule
- Parser
- ParseOption
- Chain
- JobWrapper
- Job
- FuncJob
- JobWithContext
- FuncJobWithContext
- ErrorJob
- FuncErrorJob
- NamedJob
- MissedPolicy
- TriggerCondition
- JobResult
- Dependency
- WorkflowExecution
- Workflow
- ObservabilityHooks
- SpecAnalysis
- ValidationError
- PanicError
- Logger
- Clock
- Timer
- FakeClock
- RealClock
- Functions
- Constants
- Errors
Package cron
import "github.com/netresearch/go-cron"
Package cron implements a cron spec parser and job runner.
Types
Cron
type Cron struct {
// contains filtered or unexported fields
}
Cron keeps track of any number of entries, invoking the associated func as specified by the schedule. It may be started, stopped, and the entries may be inspected while running.
Entries are stored in a min-heap ordered by next execution time, providing O(log n) insertion/removal and O(1) access to the next entry to run.
func New
func New(opts ...Option) *Cron
New returns a new Cron job runner, modified by the given options.
Default Settings:
- Time Zone:
time.Local - Parser: Standard 5-field cron (minute, hour, dom, month, dow)
- Chain: Recovers panics and logs to stderr
- Clock:
RealClock{}(real system time)
Example:
c := cron.New()
c := cron.New(cron.WithSeconds())
c := cron.New(cron.WithLocation(time.UTC))
func (*Cron) AddFunc
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error)
AddFunc adds a func to the Cron to be run on the given schedule. The spec is parsed using the time zone of this Cron instance as the default. An opaque ID is returned that can be used to later remove it.
Example:
id, err := c.AddFunc("30 * * * *", func() {
fmt.Println("Every hour on the half hour")
})
func (*Cron) AddJob
func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error)
AddJob adds a Job to the Cron to be run on the given schedule. The spec is parsed using the time zone of this Cron instance as the default. An opaque ID is returned that can be used to later remove it.
Example:
type MyJob struct{}
func (j MyJob) Run() { fmt.Println("Running") }
id, err := c.AddJob("@hourly", MyJob{})
func (*Cron) Schedule
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID
Schedule adds a Job to the Cron to be run on the given schedule. The job is wrapped with the configured Chain.
Example:
id := c.Schedule(cron.Every(time.Hour), cron.FuncJob(func() {
fmt.Println("Every hour")
}))
func (*Cron) UpdateJob
func (c *Cron) UpdateJob(id EntryID, spec string) error
UpdateJob updates the schedule of an existing entry, parsing the provided cron spec.
Returns ErrEntryNotFound if the entry does not exist.
Returns a parse error if the spec is invalid for the configured parser.
If the scheduler is running, the update is applied safely via the run loop and takes effect immediately for next-run computation. If stopped, the schedule is updated directly in place.
Example:
id, _ := c.AddFunc("0 9 * * *", dailyReport)
// Change to run at 10:30 every day
if err := c.UpdateJob(id, "30 10 * * *"); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateJobByName
func (c *Cron) UpdateJobByName(name, spec string) error
UpdateJobByName updates the schedule of an existing entry identified by its name,
parsing the provided cron spec.
Returns ErrEntryNotFound if the entry does not exist.
Returns a parse error if the spec is invalid for the configured parser.
If the scheduler is running, the actual update is delegated to UpdateSchedule
which routes through the run loop safely.
Example:
c.AddFunc("0 9 * * *", dailyReport, cron.WithName("daily-report"))
// Change to run at 10:30 every day
if err := c.UpdateJobByName("daily-report", "30 10 * * *"); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateSchedule
func (c *Cron) UpdateSchedule(id EntryID, schedule Schedule) error
UpdateSchedule updates the schedule of an existing entry.
Returns ErrEntryNotFound if the entry does not exist.
If the scheduler is running, the entry is re-positioned in the heap according to
its new Next time; if stopped, the new schedule will apply when started.
Notes:
- Preserves
WrappedJob,Job, name, tags, and missed-run settings - Recomputes
Nextfromclock.Now();Previs unchanged
Example:
id, _ := c.AddFunc("0 9 * * *", dailyReport)
// Change to run at 10:30 every day
schedule, _ := cron.ParseStandard("30 10 * * *")
if err := c.UpdateSchedule(id, schedule); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateScheduleByName
func (c *Cron) UpdateScheduleByName(name string, schedule Schedule) error
UpdateScheduleByName updates the Schedule of an existing entry identified by its name.
Returns ErrEntryNotFound if the entry does not exist.
Lookup is O(1) via the internal name index. If the scheduler is running,
the actual update is delegated to UpdateSchedule which routes through the
run loop safely.
Example:
c.AddFunc("0 9 * * *", dailyReport, cron.WithName("daily-report"))
// Change to run at 10:30 every day
schedule, _ := cron.ParseStandard("30 10 * * *")
if err := c.UpdateScheduleByName("daily-report", schedule); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateEntry
func (c *Cron) UpdateEntry(id EntryID, schedule Schedule, job Job) error
UpdateEntry atomically replaces both the Schedule and the Job of an existing entry. The new job is re-wrapped through the configured Chain, so middleware (Recover, SkipIfStillRunning, etc.) is applied to the replacement job.
When the job is replaced, the old entry's per-entry context is canceled (signaling
any running JobWithContext to stop), and a fresh context is created for the new job.
This eliminates the need for callers to manage their own context.WithCancel per
reschedule.
Returns ErrEntryNotFound if the entry does not exist.
Returns ErrNilJob if job is nil (use UpdateSchedule to update only the schedule).
Concurrency semantics are the same as UpdateSchedule.
Example:
id, _ := c.AddFunc("0 9 * * *", dailyReport)
// Replace both schedule and job atomically
// The old job's context is automatically canceled.
if err := c.UpdateEntry(id, cron.Every(5*time.Second), cron.FuncJob(func() {
doWork()
})); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateEntryByName
func (c *Cron) UpdateEntryByName(name string, schedule Schedule, job Job) error
UpdateEntryByName atomically replaces both the Schedule and the Job of an existing
entry identified by its Name. Lookup is O(1) via the internal name index.
Delegates to UpdateEntry for the actual update.
Returns ErrEntryNotFound if the entry does not exist.
Returns ErrNilJob if job is nil.
Example:
c.AddFunc("0 9 * * *", dailyReport, cron.WithName("daily-report"))
// Replace both schedule and job by name
if err := c.UpdateEntryByName("daily-report", cron.Every(5*time.Second), newJob); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateEntryJob
func (c *Cron) UpdateEntryJob(id EntryID, spec string, job Job) error
UpdateEntryJob parses spec with the Cron's configured parser, then atomically replaces both schedule and job. This eliminates the need for callers to construct their own parser matching the Cron's configuration.
Returns a parse error if spec is invalid for the configured parser.
Returns ErrEntryNotFound if the entry does not exist.
Returns ErrNilJob if job is nil.
Example:
id, _ := c.AddFunc("0 9 * * *", dailyReport)
// Update both schedule and job using a spec string
if err := c.UpdateEntryJob(id, "@every 5m", newJob); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpdateEntryJobByName
func (c *Cron) UpdateEntryJobByName(name, spec string, job Job) error
UpdateEntryJobByName is the name-based variant of UpdateEntryJob.
Parses spec with the Cron's configured parser, then atomically replaces both
schedule and job of the entry identified by name.
Returns a parse error if spec is invalid for the configured parser.
Returns ErrEntryNotFound if the entry does not exist.
Returns ErrNilJob if job is nil.
Example:
c.AddFunc("0 9 * * *", dailyReport, cron.WithName("daily-report"))
// Update both schedule and job by name using a spec string
if err := c.UpdateEntryJobByName("daily-report", "@every 5m", newJob); err != nil {
log.Println("update failed:", err)
}
func (*Cron) UpsertJob
func (c *Cron) UpsertJob(spec string, cmd Job, opts ...JobOption) (EntryID, error)
UpsertJob creates or updates a named job entry. If an entry with the given
name already exists, its schedule and job are atomically replaced via
UpdateEntry. If no entry exists, a new one is created via ScheduleJob.
A WithName option is required. Returns ErrNameRequired if no name is provided.
This eliminates the common "try update, fallback to add" boilerplate:
Example:
// Before (manual upsert):
if err := c.UpdateEntryJobByName(name, spec, job); errors.Is(err, cron.ErrEntryNotFound) {
c.AddJob(spec, job, cron.WithName(name))
}
// After:
id, err := c.UpsertJob(spec, job, cron.WithName(name))
func (*Cron) DrainAndUpsertJob
func (c *Cron) DrainAndUpsertJob(spec string, cmd Job, opts ...JobOption) (EntryID, error)
DrainAndUpsertJob is the windowless variant of UpsertJob. For an existing
named entry it pauses the entry, waits for any in-flight invocation to finish,
replaces the schedule and job, then restores the entry's prior paused state.
Pausing before the drain guarantees the old schedule cannot fire again between
the wait and the replacement — closing the gap present in the two-step
WaitForJobByName + UpsertJob sequence. If no entry with the given name
exists it creates one (no stale schedule ⇒ no window). Same options and errors
as UpsertJob; a WithName option is required.
It blocks for as long as the in-flight invocation runs but holds no cron lock
while draining, so it never blocks the run loop or other mutators. It is not
atomic against other concurrent mutators of the same entry (Remove, Resume,
Trigger); those are handled gracefully but fall outside the no-stale-fire
guarantee. See ADR-022.
Example:
// Graceful, windowless reschedule in one call:
id, err := c.DrainAndUpsertJob(newSpec, newJob, cron.WithName("my-job"))
func (*Cron) WaitForJob
func (c *Cron) WaitForJob(id EntryID)
WaitForJob blocks until all currently-running invocations of the given entry complete. Returns immediately if the entry is not currently running or does not exist.
This enables graceful job replacement — wait for the current execution to finish before replacing the job:
cr.WaitForJob(id)
cr.UpsertJob(newSpec, newJob, cron.WithName("my-job"))
func (*Cron) WaitForJobByName
func (c *Cron) WaitForJobByName(name string)
WaitForJobByName is the named variant of WaitForJob. Blocks until all currently-running invocations of the named entry complete. Returns immediately if the entry is not currently running or no entry has the given name.
cr.WaitForJobByName("my-job")
cr.UpsertJob(newSpec, newJob, cron.WithName("my-job"))
Note: the two calls above are not atomic — the old schedule can fire once between them. Use
DrainAndUpsertJobfor a windowless reschedule.
func (*Cron) IsJobRunning
func (c *Cron) IsJobRunning(id EntryID) bool
IsJobRunning reports whether the entry with the given ID has any invocations currently in flight. Returns false for non-existent entries.
func (*Cron) IsJobRunningByName
func (c *Cron) IsJobRunningByName(name string) bool
IsJobRunningByName reports whether the named entry has any invocations currently in flight. Returns false if no entry has the given name.
if cr.IsJobRunningByName("my-job") {
cr.WaitForJobByName("my-job")
}
cr.UpsertJob(newSpec, newJob, cron.WithName("my-job"))
func (*Cron) PauseEntry
func (c *Cron) PauseEntry(id EntryID) error
PauseEntry temporarily suspends execution of the entry with the given ID.
While paused, the entry remains registered and its schedule advances, but
execution is skipped. Use ResumeEntry to re-enable execution.
Pausing an already-paused entry is a no-op (returns nil).
Returns ErrEntryNotFound if no entry with the given ID exists.
Example:
id, _ := c.AddFunc("@every 5m", syncData, cron.WithName("sync"))
c.Start()
// Pause during maintenance
if err := c.PauseEntry(id); err != nil {
log.Println("pause failed:", err)
}
func (*Cron) PauseEntryByName
func (c *Cron) PauseEntryByName(name string) error
PauseEntryByName temporarily suspends the entry identified by its Name.
Lookup is O(1) via the internal name index.
Returns ErrEntryNotFound if no entry with the given name exists.
Example:
c.AddFunc("@every 5m", syncData, cron.WithName("sync"))
c.PauseEntryByName("sync")
func (*Cron) ResumeEntry
func (c *Cron) ResumeEntry(id EntryID) error
ResumeEntry re-enables execution of a previously paused entry. The entry's schedule is preserved; it will execute at its next scheduled time.
Resuming an already-active entry is a no-op (returns nil).
Returns ErrEntryNotFound if no entry with the given ID exists.
Example:
// Resume after maintenance
if err := c.ResumeEntry(id); err != nil {
log.Println("resume failed:", err)
}
func (*Cron) ResumeEntryByName
func (c *Cron) ResumeEntryByName(name string) error
ResumeEntryByName re-enables execution of a previously paused entry
identified by its Name. Lookup is O(1) via the internal name index.
Returns ErrEntryNotFound if no entry with the given name exists.
Example:
c.ResumeEntryByName("sync")
func (*Cron) IsEntryPaused
func (c *Cron) IsEntryPaused(id EntryID) bool
IsEntryPaused reports whether the entry with the given ID is currently paused. Returns false if the entry does not exist.
func (*Cron) IsEntryPausedByName
func (c *Cron) IsEntryPausedByName(name string) bool
IsEntryPausedByName reports whether the named entry is currently paused. Returns false if no entry has the given name.
Example:
if c.IsEntryPausedByName("sync") {
fmt.Println("sync job is paused")
}
func (*Cron) AddDependency
func (c *Cron) AddDependency(child, parent EntryID, condition TriggerCondition) error
AddDependency adds a dependency edge: child waits for parent with the given
condition. When the parent job completes, the child is triggered if the condition
matches the parent's JobResult.
The operation is idempotent: adding an identical edge is a no-op.
Returns ErrEntryNotFound if either entry does not exist.
Returns ErrInvalidCondition if the condition is not valid.
Returns ErrCycleDetected if the edge would create a cycle.
Example:
a, _ := c.AddFunc("0 2 * * *", extract, cron.WithName("extract"))
b, _ := c.AddFunc("@triggered", transform, cron.WithName("transform"))
if err := c.AddDependency(b, a, cron.OnSuccess); err != nil {
log.Fatal(err)
}
func (*Cron) AddDependencyByName
func (c *Cron) AddDependencyByName(child, parent string, condition TriggerCondition) error
AddDependencyByName is the name-based variant of AddDependency.
Returns ErrEntryNotFound if either name does not exist.
Example:
c.AddDependencyByName("transform", "extract", cron.OnSuccess)
func (*Cron) RemoveDependency
func (c *Cron) RemoveDependency(child, parent EntryID) error
RemoveDependency removes a dependency edge between child and parent.
func (*Cron) RemoveDependencyByName
func (c *Cron) RemoveDependencyByName(child, parent string) error
RemoveDependencyByName is the name-based variant of RemoveDependency.
Returns ErrEntryNotFound if either name does not exist.
func (*Cron) Dependencies
func (c *Cron) Dependencies(id EntryID) []Dependency
Dependencies returns a copy of the dependency edges for an entry. Returns nil if the entry has no dependencies.
func (*Cron) DependenciesByName
func (c *Cron) DependenciesByName(name string) []Dependency
DependenciesByName is the name-based variant of Dependencies.
Returns nil if no entry has the given name or the entry has no dependencies.
func (*Cron) AddWorkflow
func (c *Cron) AddWorkflow(w *Workflow) error
AddWorkflow validates and registers all steps of a Workflow atomically. It parses all specs, checks for duplicate names, validates the DAG structure (no cycles, at most one final step, all After references exist), and then registers all entries and wires dependency edges. On any failure, already-registered entries are rolled back.
Failure model: The workflow engine detects job failure via panics. Since
Job.Run() has no return value, steps that need to signal errors should use
FuncErrorJob (which converts errors to panics) or wrappers like RetryOnError
/ RetryWithBackoff. The Recover wrapper is workflow-aware and re-panics in
workflow context so failures propagate correctly.
Returns:
ErrEmptyWorkflowif the workflow has no stepsErrMultipleFinalStepsif more than one step is marked FinalErrUnknownStepif a step references an unknown parent via AfterErrCycleDetectedif the step dependencies form a cycleErrDuplicateNameif a step name conflicts with an existing entry- Parse errors if any spec is invalid for the configured parser
Example:
wf := cron.NewWorkflow("etl-pipeline")
wf.StepFunc("extract", "0 2 * * *", extractData)
wf.StepFunc("transform", "@triggered", transformData).
After("extract", cron.OnSuccess)
wf.StepFunc("load", "@triggered", loadData).
After("transform", cron.OnSuccess)
wf.StepFunc("cleanup", "@triggered", cleanup).Final()
if err := c.AddWorkflow(wf); err != nil {
log.Fatal(err)
}
func (*Cron) WorkflowStatus
func (c *Cron) WorkflowStatus(executionID string) *WorkflowExecution
WorkflowStatus returns the execution state for the given workflow execution ID.
It searches active executions first, then completed executions (retained up to
the WithWorkflowRetention limit). Returns nil if no execution with the given
ID exists.
Example:
status := c.WorkflowStatus(execID)
if status != nil && status.IsComplete() {
fmt.Println("Workflow finished at", status.StartTime)
}
func (*Cron) ActiveWorkflows
func (c *Cron) ActiveWorkflows() []WorkflowExecution
ActiveWorkflows returns copies of all in-progress workflow executions. Returns an empty slice if no workflows are currently executing.
Example:
for _, wf := range c.ActiveWorkflows() {
fmt.Printf("Workflow %s started at %v\n", wf.ID, wf.StartTime)
}
func (*Cron) TriggerEntry
func (c *Cron) TriggerEntry(id EntryID) error
TriggerEntry immediately executes the entry with the given ID, regardless
of its schedule. The entry's middleware chain (Recover, SkipIfStillRunning,
etc.) is applied as usual. This works on both triggered (@triggered) and
regularly scheduled entries — providing a "run now" capability for any entry.
The scheduler must be running; returns ErrNotRunning otherwise.
Returns ErrEntryPaused if the entry is paused.
Returns ErrEntryNotFound if no entry with the given ID exists.
Example:
id, _ := c.AddFunc("@triggered", deploy, cron.WithName("deploy"))
c.Start()
c.TriggerEntry(id) // Run on demand
func (*Cron) TriggerEntryByName
func (c *Cron) TriggerEntryByName(name string) error
TriggerEntryByName immediately executes the entry identified by its Name. Lookup is O(1) via the internal name index.
Returns ErrNotRunning if the scheduler is not running.
Returns ErrEntryPaused if the entry is paused.
Returns ErrEntryNotFound if no entry with the given name exists.
Example:
c.AddFunc("@triggered", deploy, cron.WithName("deploy"))
c.Start()
c.TriggerEntryByName("deploy")
func (*Cron) ScheduleJob
func (c *Cron) ScheduleJob(schedule Schedule, cmd Job, opts ...JobOption) (EntryID, error)
ScheduleJob adds a Job to the Cron to be run on the given schedule.
The job is wrapped with the configured Chain. Unlike Schedule, returns an error
instead of silently logging on failure, and supports JobOption arguments.
Returns ErrMaxEntriesReached if the maximum entry limit has been reached.
Returns ErrDuplicateName if a name is provided and already exists.
func (*Cron) AddOnceFunc
func (c *Cron) AddOnceFunc(spec string, cmd func(), opts ...JobOption) (EntryID, error)
AddOnceFunc adds a func to run once on the given schedule, then automatically
remove itself. Convenience wrapper combining AddFunc with WithRunOnce().
func (*Cron) AddOnceJob
func (c *Cron) AddOnceJob(spec string, cmd Job, opts ...JobOption) (EntryID, error)
AddOnceJob adds a Job to run once on the given schedule, then automatically
remove itself. Convenience wrapper combining AddJob with WithRunOnce().
func (*Cron) ScheduleOnceJob
func (c *Cron) ScheduleOnceJob(schedule Schedule, cmd Job, opts ...JobOption) (EntryID, error)
ScheduleOnceJob adds a Job to run once on the given schedule, then automatically
remove itself. Convenience wrapper combining ScheduleJob with WithRunOnce().
func (*Cron) ValidateSpec
func (c *Cron) ValidateSpec(spec string) error
ValidateSpec validates a cron expression using this Cron instance's configured parser. Returns nil if valid, or an error describing the problem. Useful for pre-validating user input when the Cron uses a custom parser.
Example:
c := cron.New(cron.WithSeconds())
if err := c.ValidateSpec("0 30 * * * *"); err != nil {
return fmt.Errorf("invalid cron expression: %w", err)
}
func (*Cron) Entries
func (c *Cron) Entries() []Entry
Entries returns a snapshot of the cron entries, sorted by next execution time.
Each returned Entry is a struct copy with its Tags slice cloned, so mutating
the returned tags does not affect internal scheduler state. Other reference-typed
fields (e.g., Schedule, Job) are shallow-copied.
func (*Cron) Entry
func (c *Cron) Entry(id EntryID) Entry
Entry returns a snapshot of the given entry, or a zero Entry if not found.
This operation is O(1) using the internal index map. The returned Entry is a
struct copy with its Tags slice cloned, so mutating the returned tags does
not affect internal scheduler state.
func (*Cron) EntryByName
func (c *Cron) EntryByName(name string) Entry
EntryByName returns a snapshot of the entry with the given name,
or an invalid Entry (Entry.Valid() == false) if not found.
This operation is O(1) using the internal name index. The returned Entry is a
struct copy with its Tags slice cloned, so mutating the returned tags does
not affect internal scheduler state.
Example:
c.AddFunc("0 9 * * *", job, cron.WithName("daily-report"))
entry := c.EntryByName("daily-report")
if entry.Valid() {
fmt.Println("Next run:", entry.Next)
}
func (*Cron) EntriesByTag
func (c *Cron) EntriesByTag(tag string) []Entry
EntriesByTag returns snapshots of all entries that have the given tag. Returns an empty slice if no entries match.
Example:
c.AddFunc("0 9 * * *", job1, cron.WithTags("reports"))
c.AddFunc("0 * * * *", job2, cron.WithTags("reports"))
entries := c.EntriesByTag("reports") // Returns both entries
func (*Cron) Remove
func (c *Cron) Remove(id EntryID)
Remove an entry from being run in the future. Cancels the entry's per-entry context.
func (*Cron) RemoveByName
func (c *Cron) RemoveByName(name string) bool
RemoveByName removes the entry with the given name. Returns true if an entry was removed, false if no entry had that name.
func (*Cron) RemoveByTag
func (c *Cron) RemoveByTag(tag string) int
RemoveByTag removes all entries that have the given tag. Returns the number of entries removed.
func (*Cron) IsRunning
func (c *Cron) IsRunning() bool
IsRunning returns true if the cron scheduler is currently running.
func (*Cron) Start
func (c *Cron) Start()
Start the cron scheduler in its own goroutine, or no-op if already started.
func (*Cron) Run
func (c *Cron) Run()
Run the cron scheduler in the current goroutine (blocking), or no-op if already running.
func (*Cron) Stop
func (c *Cron) Stop() context.Context
Stop stops the cron scheduler if it is running; otherwise it does nothing. A context is returned so the caller can wait for running jobs to complete.
Example:
ctx := c.Stop()
<-ctx.Done() // Wait for all jobs to finish
func (*Cron) StopAndWait
func (c *Cron) StopAndWait()
StopAndWait stops the cron scheduler and blocks until all running jobs complete.
Equivalent to <-c.Stop().Done().
func (*Cron) StopWithTimeout
func (c *Cron) StopWithTimeout(timeout time.Duration) bool
StopWithTimeout stops the cron scheduler and waits for running jobs to complete with a timeout. Returns true if all jobs completed within the timeout, false if the timeout was reached. A timeout of zero or negative waits indefinitely.
Example:
if !c.StopWithTimeout(30 * time.Second) {
log.Println("Warning: some jobs did not complete within 30s")
}
func (*Cron) Location
func (c *Cron) Location() *time.Location
Location gets the time zone location.
Entry
type Entry struct {
ID EntryID // Cron-assigned unique identifier
Schedule Schedule // Schedule for this entry
Next time.Time // Next activation time (zero if not started)
Prev time.Time // Last run time (zero if never run)
WrappedJob Job // Job with chain wrappers applied
Job Job // Original job as submitted
Name string // Optional name for the entry
Tags []string // Optional tags for categorizing entries
MissedPolicy MissedPolicy // Policy for handling missed executions
MissedGracePeriod time.Duration // Maximum age for catch-up runs
Paused bool // Whether this entry is paused
Triggered bool // Whether this entry uses a triggered schedule
}
Entry consists of a schedule and the func to execute on that schedule.
Per-entry context: Each entry has its own context.Context derived from the
Cron's base context. Jobs implementing JobWithContext receive this per-entry
context, enabling fine-grained cancellation:
- Remove/RemoveByName: cancels the removed entry's context
- UpdateEntry (with new job): cancels the old context, creates a fresh one
- UpdateSchedule (schedule-only): does NOT cancel the context
- Stop(): cancels the base context, which cascades to all entry contexts
func (Entry) Valid
func (e Entry) Valid() bool
Valid returns true if this is not the zero entry.
func (Entry) Run
func (e Entry) Run()
Run executes the entry's job through the configured chain wrappers.
Use this instead of Entry.Job.Run() when chain behavior is needed.
EntryID
type EntryID uint64
EntryID identifies an entry within a Cron instance. Using uint64 prevents overflow and ID collisions on all platforms.
MissedPolicy
type MissedPolicy int
const (
MissedSkip MissedPolicy = iota // Do not catch up on missed executions (default)
MissedRunOnce // Run once for the most recent missed execution
MissedRunAll // Run for every missed execution (capped at 100)
)
MissedPolicy defines how to handle jobs that were scheduled to run while the scheduler was not running (e.g., application restart).
Important: This feature requires the user to provide the last run time via
WithPrev(). The scheduler does NOT persist state - users are responsible for
storing and loading last run times from their own persistence layer.
Policies:
MissedSkip: Default behavior. No catch-up runs occur.MissedRunOnce: Run the job once immediately for the most recent missed time.MissedRunAll: Run the job for every missed execution (up to 100 runs for safety).
Example:
// Load last run time from your database
lastRun := loadFromDatabase("daily-report")
c.AddFunc("0 9 * * *", dailyReport,
cron.WithPrev(lastRun), // When it last ran
cron.WithMissedPolicy(cron.MissedRunOnce), // Run once if missed
cron.WithMissedGracePeriod(2*time.Hour), // Only if within 2 hours
)
See PERSISTENCE_GUIDE.md for complete integration patterns.
JobOption
type JobOption func(*Entry)
JobOption represents a modification to a specific job entry.
Job options are passed to AddFunc, AddJob, or Schedule methods.
func WithName
func WithName(name string) JobOption
WithName sets a name for the job entry. Named jobs can be looked up via
EntryByName() and filtered via EntriesByTag().
Example:
c.AddFunc("0 9 * * *", dailyReport, cron.WithName("daily-report"))
entry := c.EntryByName("daily-report")
func WithPrev
func WithPrev(t time.Time) JobOption
WithPrev sets the last execution time for the job entry. This is used to
calculate missed executions when combined with WithMissedPolicy().
Example:
lastRun := loadFromDatabase("my-job")
c.AddFunc("0 * * * *", myJob,
cron.WithPrev(lastRun),
cron.WithMissedPolicy(cron.MissedRunOnce),
)
func WithMissedPolicy
func WithMissedPolicy(policy MissedPolicy) JobOption
WithMissedPolicy sets the policy for handling missed job executions.
See MissedPolicy for available options.
func WithMissedGracePeriod
func WithMissedGracePeriod(d time.Duration) JobOption
WithMissedGracePeriod sets the maximum age for missed executions to be
caught up. Missed runs older than this duration are skipped even with
MissedRunAll policy.
Example:
// Only catch up on missed runs from the last hour
c.AddFunc("*/5 * * * *", job,
cron.WithPrev(lastRun),
cron.WithMissedPolicy(cron.MissedRunAll),
cron.WithMissedGracePeriod(time.Hour),
)
func WithTags
func WithTags(tags ...string) JobOption
WithTags sets tags for categorizing the job entry. Multiple entries can share the same tags, enabling group operations.
Example:
c.AddFunc("* * * * *", job1, cron.WithTags("reports", "daily"))
c.AddFunc("0 * * * *", job2, cron.WithTags("reports", "hourly"))
entries := c.EntriesByTag("reports")
Option
type Option func(*Cron)
Option represents a modification to the default behavior of a Cron.
func WithLocation
func WithLocation(loc *time.Location) Option
WithLocation overrides the timezone of the cron instance.
func WithSeconds
func WithSeconds() Option
WithSeconds overrides the parser to include a seconds field as the first field. Equivalent to Quartz scheduler format.
func WithParser
func WithParser(p ScheduleParser) Option
WithParser overrides the parser used for interpreting job schedules.
func WithChain
func WithChain(wrappers ...JobWrapper) Option
WithChain specifies Job wrappers to apply to all jobs added to this cron.
func WithLogger
func WithLogger(logger Logger) Option
WithLogger uses the provided logger.
func WithClock
func WithClock(clock Clock) Option
WithClock uses the provided Clock implementation instead of RealClock. Useful for testing time-dependent behavior without waiting.
Example:
// For testing with FakeClock
fakeClock := cron.NewFakeClock(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC))
c := cron.New(cron.WithClock(fakeClock))
c.Start()
fakeClock.Advance(time.Hour) // Trigger jobs deterministically
// RealClock is used by default, no need to specify unless overriding
func WithContext
func WithContext(ctx context.Context) Option
WithContext sets the parent context from which the cron scheduler derives its
own cancelable child context (via context.WithCancel) for all job executions.
Stop() cancels only that derived child context, signaling all running
JobWithContext jobs to shut down; the caller-provided context is not canceled
by Stop(). If not specified, context.Background() is used as the parent.
Example:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := cron.New(cron.WithContext(ctx))
func WithMaxEntries
func WithMaxEntries(maxEntries int) Option
WithMaxEntries limits the maximum number of entries. When the limit is reached,
AddFunc and AddJob return ErrMaxEntriesReached. A limit of 0 means unlimited.
func WithObservability
func WithObservability(hooks ObservabilityHooks) Option
WithObservability configures observability hooks for monitoring cron operations.
func WithSecondOptional
func WithSecondOptional() Option
WithSecondOptional overrides the parser to accept an optional seconds field. Expressions can have either 5 fields (standard) or 6 fields (with seconds).
func WithMinEveryInterval
func WithMinEveryInterval(d time.Duration) Option
WithMinEveryInterval configures the minimum interval allowed for @every expressions.
Default is 1 second. Set to 0 for sub-second intervals (useful for testing).
func WithMaxSearchYears
func WithMaxSearchYears(years int) Option
WithMaxSearchYears configures how far into the future schedule matching searches before giving up. Default is 5 years.
func WithRunImmediately
func WithRunImmediately() JobOption
WithRunImmediately causes the job to run immediately upon registration, then follow the normal schedule thereafter.
func WithRunOnce
func WithRunOnce() JobOption
WithRunOnce causes the job to be automatically removed after its first execution.
func WithPaused
func WithPaused() JobOption
WithPaused causes the entry to be added in a paused state. Paused entries remain
registered with their schedule intact but are skipped during execution. Use
ResumeEntry or ResumeEntryByName to activate the entry later.
This is useful for pre-registering jobs that should only run after explicit activation, maintenance windows, or feature-flagged jobs.
Example:
id, _ := c.AddFunc("@every 5m", syncData, cron.WithPaused(), cron.WithName("sync"))
// Later, when ready:
c.ResumeEntry(id)
func WithCapacity
func WithCapacity(n int) Option
WithCapacity pre-allocates internal data structures for the expected number of entries. This reduces map rehashing and slice growth during bulk additions, improving performance when adding many jobs at startup.
Pre-allocates:
entryIndexmap with capacity n (O(1) lookup by ID)nameIndexmap with capacity n (O(1) lookup by name)entriesheap slice with capacity n
For applications adding fewer than 100 jobs, the default allocation is sufficient. Use this option when bulk-loading hundreds or thousands of jobs.
A capacity of 0 or negative has no effect (uses default allocation).
Example:
// Expect ~1000 jobs at startup
c := cron.New(cron.WithCapacity(1000))
for _, job := range jobs {
c.AddFunc(job.Schedule, job.Func)
}
func WithWorkflowRetention
func WithWorkflowRetention(n int) Option
WithWorkflowRetention sets the maximum number of completed workflow executions
to retain for query via WorkflowStatus. Default is 100. Set to 0 for unlimited
retention (not recommended for long-running services).
Example:
c := cron.New(cron.WithWorkflowRetention(50))
TriggerCondition
type TriggerCondition int
const (
OnSuccess TriggerCondition = iota // Parent completed without panicking
OnFailure // Parent panicked (use FuncErrorJob to convert errors)
OnSkipped // Parent was skipped (condition not met)
OnComplete // Parent resolved to any terminal state
)
TriggerCondition defines when a dependent job should be triggered relative to
its parent's outcome. Used with AddDependency and the Workflow builder's
After method.
func (TriggerCondition) String
func (c TriggerCondition) String() string
String returns the human-readable name ("OnSuccess", "OnFailure", "OnSkipped", "OnComplete").
func (TriggerCondition) Valid
func (c TriggerCondition) Valid() bool
Valid reports whether c is a known trigger condition.
func (TriggerCondition) Matches
func (c TriggerCondition) Matches(result JobResult) bool
Matches reports whether the given parent result satisfies this condition.
OnComplete matches any terminal result; the others match their specific JobResult.
JobResult
type JobResult int
const (
ResultPending JobResult = iota // Job has not yet completed
ResultSuccess // Job completed without error
ResultFailure // Job failed (error or panic)
ResultSkipped // Job was skipped (condition not met)
)
JobResult represents the outcome of a job within a workflow execution.
func (JobResult) String
func (r JobResult) String() string
String returns the human-readable name ("Pending", "Success", "Failure", "Skipped").
func (JobResult) IsTerminal
func (r JobResult) IsTerminal() bool
IsTerminal reports whether the result represents a final state. Returns true for
ResultSuccess, ResultFailure, and ResultSkipped; false for ResultPending.
Dependency
type Dependency struct {
ParentID EntryID
Condition TriggerCondition
}
Dependency represents a directed edge in the workflow DAG. The child entry
waits for ParentID to resolve, then fires if Condition matches the parent's
JobResult. Returned by Dependencies and DependenciesByName.
WorkflowExecution
type WorkflowExecution struct {
ID string
RootID EntryID
StartTime time.Time
Results map[EntryID]JobResult
}
WorkflowExecution tracks the state of a single workflow run. RootID is the
entry whose completion triggered the execution. Results maps each participating
entry to its current JobResult.
func (*WorkflowExecution) IsComplete
func (we *WorkflowExecution) IsComplete() bool
IsComplete reports whether every job in the execution has reached a terminal state.
Workflow
type Workflow struct {
Name string
// contains filtered or unexported fields
}
Workflow defines a multi-step DAG of named jobs with dependency edges. Use
NewWorkflow to create a workflow, then Step/StepFunc to add steps, and
AddWorkflow on a Cron instance to register it atomically.
func NewWorkflow
func NewWorkflow(name string) *Workflow
NewWorkflow creates a new Workflow with the given name.
Example:
wf := cron.NewWorkflow("etl-pipeline")
wf.StepFunc("extract", "0 2 * * *", extractData)
wf.StepFunc("transform", "@triggered", transformData).
After("extract", cron.OnSuccess)
err := c.AddWorkflow(wf)
func (*Workflow) Step
func (w *Workflow) Step(name, spec string, job Job) *WorkflowStep
Step adds a named step to the workflow with the given schedule spec and Job.
func (*Workflow) StepFunc
func (w *Workflow) StepFunc(name, spec string, fn func()) *WorkflowStep
StepFunc adds a named step with a plain function as its job.
func (*WorkflowStep) After
func (s *WorkflowStep) After(parentName string, condition TriggerCondition) *WorkflowStep
After declares that this step depends on the named parent step with the given
condition. Multiple After calls can be chained to create fan-in dependencies.
Example:
wf.StepFunc("load", "@triggered", loadData).
After("transform", cron.OnSuccess).
After("validate", cron.OnSuccess) // fan-in: both must succeed
func (*WorkflowStep) Final
func (s *WorkflowStep) Final() *WorkflowStep
Final marks this step as a finalization step. A final step receives an
OnComplete edge from every non-final step, ensuring it runs after all other
steps have resolved regardless of their outcome. At most one step per workflow
may be marked Final.
Schedule
type Schedule interface {
Next(time.Time) time.Time
}
Schedule describes a job's duty cycle. Implementations must return the next activation time, later than the given time.
ScheduleWithPrev
type ScheduleWithPrev interface {
Schedule
Prev(time.Time) time.Time
}
ScheduleWithPrev is an optional interface that schedules can implement to support backward time traversal. This is useful for detecting missed executions or determining the last scheduled run time.
Built-in schedules (SpecSchedule, ConstantDelaySchedule) implement this interface.
Custom Schedule implementations may optionally implement it.
Usage:
schedule, _ := cron.ParseStandard("0 9 * * *")
// Type assert to access Prev()
if sp, ok := schedule.(cron.ScheduleWithPrev); ok {
prev := sp.Prev(time.Now())
fmt.Println("Last scheduled run:", prev)
}
ScheduleParser
type ScheduleParser interface {
Parse(spec string) (Schedule, error)
}
ScheduleParser is an interface for schedule spec parsers.
SpecSchedule
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
Location *time.Location
}
SpecSchedule represents a cron schedule defined by bit fields for each time component.
func (*SpecSchedule) Next
func (s *SpecSchedule) Next(t time.Time) time.Time
Next returns the next activation time after the given time. Returns zero time if no valid time exists.
ConstantDelaySchedule
type ConstantDelaySchedule struct {
Delay time.Duration
}
ConstantDelaySchedule represents a simple recurring duty cycle.
func Every
func Every(duration time.Duration) ConstantDelaySchedule
Every returns a crontab Schedule that activates once every duration. Delays of less than 1 second are not supported (rounds up to 1 second).
Example:
c.Schedule(cron.Every(5*time.Minute), job)
func (ConstantDelaySchedule) Next
func (s ConstantDelaySchedule) Next(t time.Time) time.Time
Next returns the next activation time after t.
TriggeredSchedule
type TriggeredSchedule struct{}
TriggeredSchedule is a schedule that never fires automatically. Entries using
this schedule remain dormant until explicitly triggered via TriggerEntry or
TriggerEntryByName. Created by the @triggered, @manual, or @none descriptors.
func (TriggeredSchedule) Next
func (TriggeredSchedule) Next(time.Time) time.Time
Next always returns the zero time.
func (TriggeredSchedule) Prev
func (TriggeredSchedule) Prev(time.Time) time.Time
Prev always returns the zero time.
func IsTriggered
func IsTriggered(s Schedule) bool
IsTriggered reports whether the given schedule is a TriggeredSchedule.
Example:
if cron.IsTriggered(entry.Schedule) {
fmt.Println("This entry only runs when triggered manually")
}
Parser
type Parser struct {
// contains filtered or unexported fields
}
Parser parses cron specs into Schedule objects.
func NewParser
func NewParser(options ParseOption) Parser
NewParser creates a Parser with custom options.
Example:
// Quartz-style with seconds
parser := cron.NewParser(
cron.Second | cron.Minute | cron.Hour |
cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)
func (Parser) Parse
func (p Parser) Parse(spec string) (Schedule, error)
Parse returns a Schedule from a cron spec or error if invalid.
ParseOption
type ParseOption int
const (
Second ParseOption = 1 << iota // Seconds field, required
SecondOptional // Seconds field, optional
Minute // Minutes field
Hour // Hours field
Dom // Day of month field
Month // Month field
Dow // Day of week field
DowOptional // Day of week, optional
Descriptor // Enable @hourly, @every, etc.
)
ParseOption represents parser configuration flags.
Chain
type Chain struct {
// contains filtered or unexported fields
}
Chain is a sequence of JobWrappers.
func NewChain
func NewChain(c ...JobWrapper) Chain
NewChain returns a Chain of the given JobWrappers.
func (Chain) Then
func (c Chain) Then(j Job) Job
Then applies all wrappers to the given job and returns the wrapped job.
JobWrapper
type JobWrapper func(Job) Job
JobWrapper is a function that wraps a Job with additional behavior.
All chain wrappers implement JobWithContext and propagate the incoming context
to inner jobs that also implement JobWithContext. This means per-entry context
flows through the entire wrapper chain to context-aware jobs.
func RunJob
func RunJob(ctx context.Context, j Job)
RunJob executes the job. If the job implements JobWithContext, it is called
with the provided context. Otherwise, its Run method is called.
This is a helper intended for use in custom JobWrapper implementations.
func Recover
func Recover(logger Logger, opts ...RecoverOption) JobWrapper
Recover catches panics in jobs, logs them, and continues. Propagates context to context-aware inner jobs.
Workflow-aware: When running inside a workflow execution, Recover logs the panic and then re-panics so the workflow engine correctly detects the failure. The scheduler catches the re-panic without crashing.
func SkipIfStillRunning
func SkipIfStillRunning(logger Logger) JobWrapper
SkipIfStillRunning skips a job invocation if the previous one is still running. Propagates context to context-aware inner jobs.
func DelayIfStillRunning
func DelayIfStillRunning(logger Logger) JobWrapper
DelayIfStillRunning delays a job invocation until the previous one completes. Propagates context to context-aware inner jobs.
func Timeout
func Timeout(logger Logger, timeout time.Duration, opts ...TimeoutOption) JobWrapper
Timeout wraps a job with a timeout using the abandonment model. Propagates context to context-aware inner jobs.
Example:
c := cron.New(cron.WithChain(
cron.Timeout(logger, 30*time.Second),
cron.Recover(logger),
))
func Jitter
func Jitter(maxJitter time.Duration) JobWrapper
Jitter adds a random delay before job execution to prevent thundering herd. Propagates context to context-aware inner jobs.
func JitterWithLogger
func JitterWithLogger(logger Logger, maxJitter time.Duration) JobWrapper
JitterWithLogger is like Jitter but logs the applied delay. Propagates context to context-aware inner jobs.
func MaxConcurrent
func MaxConcurrent(n int) JobWrapper
MaxConcurrent limits the total number of jobs that can run concurrently across all entries wrapped by this chain. When all slots are occupied, new job executions wait until a slot becomes available or the context is canceled.
Note: waiting goroutines still accumulate. Use MaxConcurrentSkip to drop excess
executions instead. Unlike SkipIfStillRunning (per-job), this limits across all
jobs sharing the same wrapper instance. Panics if n <= 0. Propagates context to
context-aware inner jobs.
Example:
c := cron.New(cron.WithChain(
cron.Recover(logger),
cron.MaxConcurrent(10),
))
func MaxConcurrentSkip
func MaxConcurrentSkip(logger Logger, n int) JobWrapper
MaxConcurrentSkip is like MaxConcurrent but skips execution instead of
waiting when the concurrency limit is reached. Logs skips at Info level.
Panics if n <= 0. Propagates context to context-aware inner jobs.
Example:
c := cron.New(cron.WithChain(
cron.Recover(logger),
cron.MaxConcurrentSkip(logger, 5),
))
func RetryWithBackoff
func RetryWithBackoff(logger Logger, maxRetries int, initialDelay, maxDelay time.Duration, multiplier float64, opts ...RetryOption) JobWrapper
RetryWithBackoff wraps a job to retry on panic with exponential backoff.
maxRetries: 0 = no retries, >0 = retry up to N times, -1 = unlimitedinitialDelay: First retry delaymaxDelay: Maximum delay capmultiplier: Delay multiplier per retry (typically 2.0)opts: OptionalRetryOptionvalues (e.g.,WithRetryCallback)
Jitter of +/-10% is applied to prevent thundering herd.
Example:
c := cron.New(cron.WithChain(
cron.Recover(logger),
cron.RetryWithBackoff(logger, 3, time.Second, time.Minute, 2.0),
))
func RetryOnError
func RetryOnError(logger Logger, maxRetries int, initialDelay, maxDelay time.Duration, multiplier float64, opts ...RetryOption) JobWrapper
RetryOnError wraps an ErrorJob to retry on returned errors with exponential backoff.
Unlike RetryWithBackoff which catches panics, this wrapper uses Go-idiomatic error
returns. Jobs must implement ErrorJob; regular Job implementations are passed through
unchanged.
Example:
c := cron.New(cron.WithChain(
cron.Recover(logger),
cron.RetryOnError(logger, 3, time.Second, time.Minute, 2.0),
))
c.AddJob("@every 5m", cron.FuncErrorJob(func() error {
return callAPI()
}))
type RetryAttempt
type RetryAttempt struct {
Attempt int // 1-based attempt number
Delay time.Duration // Delay before this attempt (0 for first)
Err any // Panic value (RetryWithBackoff) or error (RetryOnError)
WillRetry bool // True if another attempt will follow
}
RetryAttempt contains metadata about a single retry attempt, passed to the
callback configured via WithRetryCallback.
type RetryOption
type RetryOption func(*retryConfig)
RetryOption configures optional behavior for RetryWithBackoff and RetryOnError.
func WithRetryCallback
func WithRetryCallback(fn func(RetryAttempt)) RetryOption
WithRetryCallback sets a callback invoked after each attempt (including the initial execution). Use this for metrics, alerting, or debugging retry behavior.
Example with Prometheus:
cron.RetryWithBackoff(logger, 3, time.Second, time.Minute, 2.0,
cron.WithRetryCallback(func(a cron.RetryAttempt) {
retryCounter.WithLabelValues(fmt.Sprint(a.Attempt)).Inc()
if !a.WillRetry && a.Err != nil {
retryExhausted.Inc()
}
}),
)
func CircuitBreaker
func CircuitBreaker(logger Logger, threshold int, cooldown time.Duration, opts ...CircuitBreakerOption) JobWrapper
CircuitBreaker wraps a job to stop execution after consecutive failures.
- Closed: Normal execution. Failures increment counter.
- Open: Execution skipped for
cooldownduration afterthresholdfailures. - Half-Open: After cooldown, one execution attempted. Success closes, failure reopens.
opts: OptionalCircuitBreakerOptionvalues (e.g.,WithStateChangeCallback)
Example:
c := cron.New(cron.WithChain(
cron.Recover(logger),
cron.CircuitBreaker(logger, 5, 5*time.Minute),
))
func CircuitBreakerWithHandle
func CircuitBreakerWithHandle(logger Logger, threshold int, cooldown time.Duration, opts ...CircuitBreakerOption) (JobWrapper, *CircuitBreakerHandle)
CircuitBreakerWithHandle is like CircuitBreaker but also returns a
*CircuitBreakerHandle for querying the circuit breaker's internal state.
The handle is safe for concurrent use and can be used from health checks,
dashboards, or metrics exporters.
Example:
wrapper, handle := cron.CircuitBreakerWithHandle(logger, 5, 5*time.Minute)
c := cron.New(cron.WithChain(cron.Recover(logger), wrapper))
// In a health check endpoint:
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
if handle.State() == cron.CircuitOpen {
http.Error(w, "circuit open", http.StatusServiceUnavailable)
return
}
fmt.Fprintf(w, "ok (failures=%d)", handle.Failures())
})
type CircuitBreakerHandle
type CircuitBreakerHandle struct { /* unexported fields */ }
CircuitBreakerHandle provides read-only access to circuit breaker state. All methods are safe for concurrent use.
| Method | Returns | Description |
|---|---|---|
State() | CircuitBreakerState | Current state (Closed, Open, HalfOpen) |
Failures() | int64 | Current consecutive failure count |
LastFailure() | time.Time | Time of last failure (zero if none) |
CooldownEnds() | time.Time | When cooldown expires (zero if not open) |
type CircuitBreakerState
type CircuitBreakerState int
const (
CircuitClosed CircuitBreakerState = iota // Normal operation
CircuitOpen // Skipping execution
CircuitHalfOpen // Probing recovery
)
CircuitBreakerState represents the current state of a circuit breaker.
The String() method returns "closed", "open", or "half-open".
type CircuitBreakerEvent
type CircuitBreakerEvent struct {
OldState CircuitBreakerState // State before the transition
NewState CircuitBreakerState // State after the transition
Failures int64 // Current consecutive failure count
Err any // Panic value that caused the transition (nil on success)
}
CircuitBreakerEvent represents a state transition in the circuit breaker,
passed to the callback configured via WithStateChangeCallback.
type CircuitBreakerOption
type CircuitBreakerOption func(*circuitBreakerConfig)
CircuitBreakerOption configures optional behavior for CircuitBreaker and
CircuitBreakerWithHandle.
func WithStateChangeCallback
func WithStateChangeCallback(fn func(CircuitBreakerEvent)) CircuitBreakerOption
WithStateChangeCallback sets a callback invoked on circuit breaker state transitions (Closed→Open, Open→HalfOpen, HalfOpen→Closed, HalfOpen→Open). The callback is invoked synchronously; keep it fast.
Example with Prometheus:
cron.CircuitBreaker(logger, 5, 5*time.Minute,
cron.WithStateChangeCallback(func(e cron.CircuitBreakerEvent) {
circuitState.WithLabelValues(e.NewState.String()).Set(1)
if e.NewState == cron.CircuitOpen {
circuitTrips.Inc()
}
}),
)
func TimeoutWithContext
func TimeoutWithContext(logger Logger, timeout time.Duration, opts ...TimeoutOption) JobWrapper
TimeoutWithContext wraps a job with a timeout that supports true cancellation.
Unlike Timeout, this passes a context with deadline to JobWithContext jobs,
allowing cooperative cancellation. A 5-second grace period is allowed after
context cancellation before the goroutine is abandoned.
Example:
c := cron.New(cron.WithChain(
cron.TimeoutWithContext(cron.DefaultLogger, 5*time.Minute),
))
c.AddJob("@every 1h", cron.FuncJobWithContext(func(ctx context.Context) {
select {
case <-ctx.Done():
return // Timeout - clean up
case <-time.After(1 * time.Minute):
// Done
}
}))
Job
type Job interface {
Run()
}
Job is an interface for submitted cron jobs.
FuncJob
type FuncJob func()
FuncJob is a wrapper that turns a func() into a cron.Job.
func (FuncJob) Run
func (f FuncJob) Run()
Run calls the wrapped function.
JobWithContext
type JobWithContext interface {
Job
RunWithContext(ctx context.Context)
}
JobWithContext is an optional interface for jobs that support context.Context.
If a job implements this interface, RunWithContext is called instead of Run,
allowing the job to receive cancellation signals, respect deadlines, and access
request-scoped values.
Each entry has its own per-entry context derived from the Cron's base context. The context is canceled when the entry is removed or its job is replaced.
FuncJobWithContext
type FuncJobWithContext func(ctx context.Context)
FuncJobWithContext is a wrapper that turns a func(context.Context) into a
JobWithContext. This enables context-aware jobs using simple functions.
func (FuncJobWithContext) Run
func (f FuncJobWithContext) Run()
Run implements Job by calling RunWithContext(context.Background()).
func (FuncJobWithContext) RunWithContext
func (f FuncJobWithContext) RunWithContext(ctx context.Context)
RunWithContext implements JobWithContext.
Example:
c.AddJob("@every 1m", cron.FuncJobWithContext(func(ctx context.Context) {
for i := 0; i < 6; i++ {
if ctx.Err() != nil {
return // Entry removed or Stop() called
}
// Do a chunk of work...
time.Sleep(5 * time.Second)
}
}))
ErrorJob
type ErrorJob interface {
Job
RunE() error
}
ErrorJob is an optional interface for jobs that return errors instead of panicking.
Used by RetryOnError for Go-idiomatic error-based retry.
FuncErrorJob
type FuncErrorJob func() error
FuncErrorJob is a wrapper that turns a func() error into an ErrorJob.
func (FuncErrorJob) Run
func (f FuncErrorJob) Run()
Run implements Job by calling RunE() and panicking on error.
func (FuncErrorJob) RunE
func (f FuncErrorJob) RunE() error
RunE implements ErrorJob.
Example:
c.AddJob("@every 5m", cron.FuncErrorJob(func() error {
return callExternalAPI()
}))
NamedJob
type NamedJob interface {
Job
Name() string
}
NamedJob is an optional interface for jobs that provide a name for observability.
If implemented, the name is passed to ObservabilityHooks callbacks.
ObservabilityHooks
type ObservabilityHooks struct {
OnJobStart func(entryID EntryID, name string, scheduledTime time.Time)
OnJobComplete func(entryID EntryID, name string, duration time.Duration, recovered any)
OnSchedule func(entryID EntryID, name string, nextRun time.Time)
OnWorkflowComplete func(executionID string, rootID EntryID, results map[EntryID]JobResult)
}
ObservabilityHooks provides callbacks for monitoring cron operations. All callbacks are optional. Hooks are called asynchronously in separate goroutines to prevent slow callbacks from blocking the scheduler.
OnWorkflowComplete is called when all jobs in a workflow execution have resolved
(success, failure, or skipped). The results map is a snapshot copy, safe to read
without synchronization.
Example with Prometheus:
hooks := cron.ObservabilityHooks{
OnJobStart: func(id cron.EntryID, name string, scheduled time.Time) {
jobsStarted.WithLabelValues(name).Inc()
},
OnJobComplete: func(id cron.EntryID, name string, dur time.Duration, recovered any) {
jobDuration.WithLabelValues(name).Observe(dur.Seconds())
if recovered != nil {
jobPanics.WithLabelValues(name).Inc()
}
},
}
c := cron.New(cron.WithObservability(hooks))
SpecAnalysis
type SpecAnalysis struct {
Valid bool
Error error
NextRun time.Time
Location *time.Location
Fields map[string]string
IsDescriptor bool
Interval time.Duration
Schedule Schedule
Warnings []string
}
SpecAnalysis contains detailed information about a parsed cron specification.
Returned by AnalyzeSpec.
ValidationError
type ValidationError struct {
Message string
Field string
Value string
}
ValidationError represents a cron expression validation error with optional field and value context.
PanicError
type PanicError struct {
Value any
Stack []byte
}
PanicError wraps a panic value with the stack trace at the point of panic.
Implements error and Unwrap(). Used by RetryWithBackoff and safeExecute.
Logger
type Logger interface {
Info(msg string, keysAndValues ...interface{})
Error(err error, msg string, keysAndValues ...interface{})
}
Logger is the logging interface used by cron. Compatible with go-logr/logr.
Variables
var DefaultLogger Logger = PrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))
var DiscardLogger Logger = PrintfLogger(log.New(io.Discard, "", 0))
func PrintfLogger
func PrintfLogger(l *log.Logger) Logger
PrintfLogger wraps a *log.Logger in the cron Logger interface.
func VerbosePrintfLogger
func VerbosePrintfLogger(l *log.Logger) Logger
VerbosePrintfLogger wraps a *log.Logger with verbose output enabled.
func NewSlogLogger
func NewSlogLogger(l *slog.Logger) *SlogLogger
NewSlogLogger creates a Logger that delegates to *slog.Logger.
Clock
type Clock interface {
Now() time.Time
NewTimer(d time.Duration) Timer
}
Clock provides an abstraction over time operations, enabling deterministic testing.
Timer
type Timer interface {
C() <-chan time.Time // Channel that receives fire time
Stop() bool // Stop the timer
Reset(d time.Duration) bool // Reset to new duration
}
Timer abstracts time.Timer for testability.
RealClock
type RealClock struct{}
RealClock implements Clock using the real system time.
func (RealClock) Now
func (RealClock) Now() time.Time
Now returns the current system time.
func (RealClock) NewTimer
func (RealClock) NewTimer(d time.Duration) Timer
NewTimer creates a real time.Timer.
FakeClock
type FakeClock struct {
// contains filtered or unexported fields
}
FakeClock is a Clock implementation for testing with controlled time.
func NewFakeClock
func NewFakeClock(t time.Time) *FakeClock
NewFakeClock creates a FakeClock initialized to the given time.
func (*FakeClock) Now
func (f *FakeClock) Now() time.Time
Now returns the fake clock's current time.
func (*FakeClock) NewTimer
func (f *FakeClock) NewTimer(d time.Duration) Timer
NewTimer creates a fake timer that fires when the clock advances past its target.
func (*FakeClock) Set
func (f *FakeClock) Set(t time.Time)
Set updates the fake clock to a specific time, firing any expired timers.
func (*FakeClock) Advance
func (f *FakeClock) Advance(d time.Duration)
Advance moves the fake clock forward by the given duration, firing expired timers.
func (*FakeClock) BlockUntil
func (f *FakeClock) BlockUntil(n int)
BlockUntil blocks until at least n timers are registered with the clock. Useful for synchronizing test setup with scheduler startup.
func (*FakeClock) TimerCount
func (f *FakeClock) TimerCount() int
TimerCount returns the number of active timers.
Functions
ParseStandard
func ParseStandard(spec string) (Schedule, error)
ParseStandard returns a Schedule for the standard 5-field cron spec.
Example:
schedule, err := cron.ParseStandard("0 6 * * ?")
if err != nil {
log.Fatal(err)
}
next := schedule.Next(time.Now())
Every
func Every(duration time.Duration) ConstantDelaySchedule
Every returns a crontab Schedule that activates once every duration. Delays of less than 1 second are rounded up to 1 second.
ValidateSpec
func ValidateSpec(spec string, options ...ParseOption) error
ValidateSpec validates a cron expression without scheduling a job.
Returns nil if valid. Uses the standard parser by default; pass ParseOption
flags to customize validation (e.g., to require a seconds field).
Example:
if err := cron.ValidateSpec("0 9 * * MON-FRI"); err != nil {
return fmt.Errorf("invalid: %w", err)
}
ValidateSpecWith
func ValidateSpecWith(spec string, parser ScheduleParser) error
ValidateSpecWith validates a cron expression using any ScheduleParser implementation.
Useful with custom parsers or pre-configured Parser instances.
ValidateSpecs
func ValidateSpecs(specs []string, options ...ParseOption) map[int]error
ValidateSpecs validates multiple cron expressions at once. Returns a map of index to error for any invalid specs.
Example:
specs := []string{"* * * * *", "invalid", "0 9 * * MON-FRI"}
errs := cron.ValidateSpecs(specs)
for idx, err := range errs {
log.Printf("Spec %d invalid: %v", idx, err)
}
AnalyzeSpec
func AnalyzeSpec(spec string, options ...ParseOption) SpecAnalysis
AnalyzeSpec provides detailed analysis of a cron expression including validation status, next run time, parsed fields, timezone, and warnings.
Example:
result := cron.AnalyzeSpec("0 9 * * MON-FRI")
if result.Valid {
fmt.Println("Next run:", result.NextRun)
fmt.Println("Fields:", result.Fields)
fmt.Println("Warnings:", result.Warnings)
}
AnalyzeSpecWithHash
func AnalyzeSpecWithHash(spec string, options ParseOption, hashSeed string) SpecAnalysis
AnalyzeSpecWithHash analyzes a cron expression containing H hash expressions. The seed (e.g., job name) produces deterministic, distributed scheduling times.
WorkflowExecutionID
func WorkflowExecutionID(ctx context.Context) string
WorkflowExecutionID returns the workflow execution ID from the context, or an
empty string if the job is not part of a workflow execution. Use this inside a
JobWithContext to correlate log entries or metrics with a specific workflow run.
Example:
c.AddJob("@triggered", cron.FuncJobWithContext(func(ctx context.Context) {
if execID := cron.WorkflowExecutionID(ctx); execID != "" {
log.Printf("Running as part of workflow %s", execID)
}
}), cron.WithName("transform"))
Schedule Introspection
NextN
func NextN(schedule Schedule, t time.Time, n int) []time.Time
NextN returns the next n execution times for the schedule, starting after t. Returns nil if schedule is nil or n <= 0.
Example:
schedule, _ := cron.ParseStandard("0 9 * * MON-FRI")
times := cron.NextN(schedule, time.Now(), 10)
for _, t := range times {
fmt.Println("Next run:", t)
}
PrevN
func PrevN(schedule Schedule, t time.Time, n int) []time.Time
PrevN returns the previous n execution times for the schedule, before t.
Returns nil if schedule is nil, n <= 0, or schedule doesn't implement ScheduleWithPrev.
Times are returned in reverse chronological order (most recent first).
Stops early if Prev() returns zero time (no earlier execution exists).
Example:
schedule, _ := cron.ParseStandard("0 9 * * MON-FRI")
times := cron.PrevN(schedule, time.Now(), 10)
for _, t := range times {
fmt.Println("Previous run:", t)
}
Between
func Between(schedule Schedule, start, end time.Time) []time.Time
Between returns all execution times in the range [start, end). The end time is exclusive. Returns nil if schedule is nil.
BetweenWithLimit
func BetweenWithLimit(schedule Schedule, start, end time.Time, limit int) []time.Time
BetweenWithLimit returns execution times in the range [start, end) up to limit. If limit is 0 or negative, no limit is applied.
Count
func Count(schedule Schedule, start, end time.Time) int
Count returns the number of executions in the range [start, end).
CountWithLimit
func CountWithLimit(schedule Schedule, start, end time.Time, limit int) int
CountWithLimit counts executions in the range [start, end) up to limit.
Matches
func Matches(schedule Schedule, t time.Time) bool
Matches reports whether the given time matches the schedule.
Returns false if schedule is nil or doesn't implement ScheduleWithPrev.
Constants
MaxSpecLength
const MaxSpecLength = 1024
MaxSpecLength is the maximum allowed length for a cron spec string.
Errors
ErrEntryNotFound
var ErrEntryNotFound = errors.New("cron: entry not found")
Returned by update methods (UpdateSchedule, UpdateScheduleByName, UpdateJob,
UpdateJobByName, UpdateEntry, UpdateEntryByName, UpdateEntryJob,
UpdateEntryJobByName), pause/resume methods (PauseEntry, PauseEntryByName,
ResumeEntry, ResumeEntryByName), and trigger methods (TriggerEntry,
TriggerEntryByName) when the specified entry does not exist.
ErrNilJob
var ErrNilJob = errors.New("cron: job must not be nil; use UpdateSchedule to update only the schedule")
Returned by UpdateEntry, UpdateEntryByName, UpdateEntryJob, and
UpdateEntryJobByName when a nil job is passed.
ErrNameRequired
var ErrNameRequired = errors.New("cron: UpsertJob requires WithName option")
Returned by UpsertJob when no WithName option is provided.
ErrMaxEntriesReached
var ErrMaxEntriesReached = errors.New("cron: max entries limit reached")
Returned by AddFunc, AddJob, and ScheduleJob when the WithMaxEntries limit
has been reached.
ErrEntryPaused
var ErrEntryPaused = errors.New("cron: entry is paused")
Returned by TriggerEntry and TriggerEntryByName when attempting to trigger
a paused entry. Resume the entry first.
ErrNotRunning
var ErrNotRunning = errors.New("cron: scheduler is not running")
Returned by TriggerEntry and TriggerEntryByName when the scheduler is not
running. Start the scheduler first.
ErrDuplicateName
var ErrDuplicateName = errors.New("cron: duplicate entry name")
Returned when adding an entry with a name that already exists.
ErrEmptySpec
var ErrEmptySpec = &ValidationError{Message: "empty spec string"}
Set as the Error field on the SpecAnalysis result returned by AnalyzeSpec
and AnalyzeSpecWithHash when an empty spec string is provided.
ErrCycleDetected
var ErrCycleDetected = errors.New("cron: dependency would create a cycle")
Returned by AddDependency, AddDependencyByName, and AddWorkflow when the
new dependency edge would create a cycle in the DAG.
ErrInvalidCondition
var ErrInvalidCondition = errors.New("cron: invalid trigger condition")
Returned by AddDependency and AddDependencyByName when the provided
TriggerCondition is not a valid known value.
ErrMultipleFinalSteps
var ErrMultipleFinalSteps = errors.New("cron: workflow has multiple final steps")
Returned by AddWorkflow when more than one step in the workflow is marked Final.
ErrUnknownStep
var ErrUnknownStep = errors.New("cron: workflow step references unknown parent")
Returned by AddWorkflow when a step's After references a parent name that
does not exist in the workflow.
ErrEmptyWorkflow
var ErrEmptyWorkflow = errors.New("cron: workflow has no steps")
Returned by AddWorkflow when the workflow has no steps.
Generated: 2026-02-14