{"record":{"id":"f7cf08c027578740","repo":"hibiken/asynq","slug":"asynq-the-scheduler-is-already-running","errorCode":null,"errorMessage":"asynq: the scheduler is already running","messagePattern":"asynq: the scheduler is already running","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scheduler.go","lineNumber":278,"sourceCode":"\tif err := s.start(); err != nil {\n\t\treturn err\n\t}\n\ts.logger.Info(\"Scheduler starting\")\n\ts.logger.Infof(\"Scheduler timezone is set to %v\", s.location)\n\ts.cron.Start()\n\ts.wg.Add(1)\n\tgo s.runHeartbeater()\n\treturn nil\n}\n\n// Checks server state and returns an error if pre-condition is not met.\n// Otherwise it sets the server state to active.\nfunc (s *Scheduler) start() error {\n\ts.state.mu.Lock()\n\tdefer s.state.mu.Unlock()\n\tswitch s.state.value {\n\tcase srvStateActive:\n\t\treturn fmt.Errorf(\"asynq: the scheduler is already running\")\n\tcase srvStateClosed:\n\t\treturn fmt.Errorf(\"asynq: the scheduler has already been stopped\")\n\t}\n\ts.state.value = srvStateActive\n\treturn nil\n}\n\n// Shutdown stops and shuts down the scheduler.\nfunc (s *Scheduler) Shutdown() {\n\ts.state.mu.Lock()\n\tif s.state.value == srvStateNew || s.state.value == srvStateClosed {\n\t\t// scheduler is not running, do nothing and return.\n\t\ts.state.mu.Unlock()\n\t\treturn\n\t}\n\ts.state.value = srvStateClosed\n\ts.state.mu.Unlock()\n","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/hibiken/asynq/blob/d135f1439bee74e989b7f9b41ecd542cc87f024a/scheduler.go#L260-L296","documentation":"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.","triggerScenarios":"Calling s.Start() or s.Run() twice on the same Scheduler instance without shutting it down; concurrent Start calls racing.","commonSituations":"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.","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"],"exampleFix":"// before\nsrv.Start(); srv.Run(mux) // second start\n// after\nvar once sync.Once\nonce.Do(func(){ srv.Run(mux) })","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func canStart(s *asynq.Scheduler) bool {\n    return s != nil && s.State() == asynq.StateNew // not yet active\n}","tryCatchPattern":"var startOnce sync.Once\nstartOnce.Do(func() {\n    if err := s.Run(mux); err != nil {\n        log.Fatalf(\"scheduler start failed: %v\", err)\n    }\n})","preventionTips":["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"],"tags":["asynq","scheduler","lifecycle","double-start"],"backgroundTag":"invalid-state-transition","analyzedSha":"d135f1439bee74e989b7f9b41ecd542cc87f024a","analyzedAt":"2026-09-07T19:02:34.660Z","contentChangedAt":"2026-09-07T19:02:34.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}