hibiken/asynq · error
asynq: the scheduler is already running
Error message
asynq: the scheduler is already running
What it means
Scheduler.start rejects the transition when the scheduler's state is already srvStateActive, i.e. Start (or Run) was called while the scheduler is running. A Scheduler can only be started once from the idle state; starting an active one is a state-machine violation.
Solutions
- Call Start/Run exactly once per Scheduler; guard with sync.Once if multiple code paths may start it
- Create a new Scheduler instance if you need a fresh lifecycle
- Check s.State() before starting if your code can race
Example fix
// before
srv.Start(); srv.Run(mux) // second start
// after
var once sync.Once
once.Do(func(){ srv.Run(mux) }) Defensive patterns
Strategy: try-catch
Type guard
func canStart(s *asynq.Scheduler) bool {
return s != nil && s.State() == asynq.StateNew // not yet active
} Try / catch
var startOnce sync.Once
startOnce.Do(func() {
if err := s.Run(mux); err != nil {
log.Fatalf("scheduler start failed: %v", err)
}
}) Prevention
- Wrap Start/Run in sync.Once or a started flag
- Audit bootstrap code for duplicate start paths (init functions, main, hot-reload)
- Prefer one long-lived scheduler per process
When it happens
Trigger: Calling s.Start() or s.Run() twice on the same Scheduler instance without shutting it down; concurrent Start calls racing.
Common situations: Double invocation during app bootstrap (e.g. both an init path and a main path start the scheduler); re-running Run after temporarily stopping without Shutdown handling; test harnesses sharing a scheduler.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- asynq: the scheduler has already been stopped
- asynq: the server is already running
- PeriodicTaskConfig.Cronspec cannot be empty
- asynq
- asynq: no scheduler entry found
AI-assisted analysis of hibiken/asynq@d135f1439b (2026-09-07).
Data as JSON: /api/errors/f7cf08c027578740.
Report an issue: GitHub.
Appendix: source
Thrown at scheduler.go:278
if err := s.start(); err != nil {
return err
}
s.logger.Info("Scheduler starting")
s.logger.Infof("Scheduler timezone is set to %v", s.location)
s.cron.Start()
s.wg.Add(1)
go s.runHeartbeater()
return nil
}
// Checks server state and returns an error if pre-condition is not met.
// Otherwise it sets the server state to active.
func (s *Scheduler) start() error {
s.state.mu.Lock()
defer s.state.mu.Unlock()
switch s.state.value {
case srvStateActive:
return fmt.Errorf("asynq: the scheduler is already running")
case srvStateClosed:
return fmt.Errorf("asynq: the scheduler has already been stopped")
}
s.state.value = srvStateActive
return nil
}
// Shutdown stops and shuts down the scheduler.
func (s *Scheduler) Shutdown() {
s.state.mu.Lock()
if s.state.value == srvStateNew || s.state.value == srvStateClosed {
// scheduler is not running, do nothing and return.
s.state.mu.Unlock()
return
}
s.state.value = srvStateClosed
s.state.mu.Unlock()
View on GitHub (pinned to d135f1439b)