{"record":{"id":"aabee3dadb74cf94","repo":"ory/hydra","slug":"watcher-is-not-running","errorCode":null,"errorMessage":"watcher is not running","messagePattern":"watcher is not running","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/watcherx/definitions.go","lineNumber":36,"sourceCode":"\tEventChannel chan Event\n\tWatcher      interface {\n\t\t// DispatchNow fires the watcher and causes an event.\n\t\t//\n\t\t// WARNING: The returned channel must be read or no further events will\n\t\t// be propagated due to a deadlock.\n\t\tDispatchNow() (<-chan int, error)\n\t}\n\tdispatcher struct {\n\t\tctx     context.Context\n\t\ttrigger chan struct{}\n\t\tdone    chan int\n\t}\n)\n\nvar (\n\t// ErrSchemeUnknown is just for checking with errors.Is()\n\tErrSchemeUnknown     = &errSchemeUnknown{}\n\tErrWatcherNotRunning = fmt.Errorf(\"watcher is not running\")\n)\n\nfunc (e *errSchemeUnknown) Is(other error) bool {\n\t_, ok := other.(*errSchemeUnknown)\n\treturn ok\n}\n\nfunc (e *errSchemeUnknown) Error() string {\n\treturn fmt.Sprintf(\"unknown scheme '%s' to watch\", e.scheme)\n}\n\nfunc newDispatcher(ctx context.Context) *dispatcher {\n\treturn &dispatcher{\n\t\tctx:     ctx,\n\t\ttrigger: make(chan struct{}),\n\t\tdone:    make(chan int),\n\t}\n}","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/watcherx/definitions.go#L18-L54","documentation":"ErrWatcherNotRunning is a sentinel error indicating the watcher's dispatcher has no trigger, i.e. the watcher was never started or its context has already been cancelled. DispatchNow returns it when asked to fire the dispatch signal but no watcher is running to dispatch.","triggerScenarios":"Calling DispatchNow() before Watcher/W Dispatch has been started, or after the watcher's context was cancelled; the second usage also returns it when ctx.Done() fires while waiting.","commonSituations":"Application shutdown racing with a DispatchNow call, forgetting to start the watcher before using the dispatcher, or unit tests calling DispatchNow on a zero-value dispatcher.","solutions":["Start the watcher (call its Watch/run method) before invoking DispatchNow","Check the watcher's context lifecycle — recreate or restart the watcher if its context was cancelled","Guard DispatchNow calls with a check that the watcher is running (e.g. use errors.Is(err, ErrWatcherNotRunning) to handle gracefully)","Fix shutdown ordering so DispatchNow is not called after context cancellation"],"exampleFix":"// before\nch, err := dispatcher.DispatchNow() // watcher never started -> ErrWatcherNotRunning\n// after\nif err := watcher.Watch(ctx); err != nil { return err }\nch, err := dispatcher.DispatchNow()\nif errors.Is(err, watcherx.ErrWatcherNotRunning) {\n    // handle: watcher stopped\n}","handlingStrategy":"type-guard","validationCode":"if watcher == nil || !watcherRunning {\n    return errors.New(\"watcher not started; cannot DispatchNow\")\n}","typeGuard":"func isWatcherNotRunning(err error) bool {\n    return errors.Is(err, watcherx.ErrWatcherNotRunning)\n}","tryCatchPattern":"ch, err := dispatcher.DispatchNow()\nif errors.Is(err, watcherx.ErrWatcherNotRunning) {\n    log.Warn(\"dispatch requested but watcher is not running; skipping\")\n    return nil\n}\n<-ch","preventionTips":["Always start the watcher before calling DispatchNow","Use errors.Is against the sentinel to handle shutdown races gracefully","Structure shutdown so DispatchNow is not invoked after context cancellation"],"tags":["watcher","lifecycle","context-cancelled"],"backgroundTag":"watcher-not-running","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}