{"record":{"id":"ff5ae708e065891f","repo":"micro/go-micro","slug":"watcher-stopped-ff5ae7","errorCode":null,"errorMessage":"watcher stopped","messagePattern":"watcher stopped","errorType":"exception","errorClass":"ErrWatcherStopped","httpStatus":null,"severity":"info","filePath":"registry/registry.go","lineNumber":12,"sourceCode":"// Package registry is an interface for service discovery\npackage registry\n\nimport (\n\t\"errors\"\n)\n\nvar (\n\t// Not found error when GetService is called.\n\tErrNotFound = errors.New(\"service not found\")\n\t// Watcher stopped error when watcher is stopped.\n\tErrWatcherStopped = errors.New(\"watcher stopped\")\n)\n\n// The registry provides an interface for service discovery\n// and an abstraction over varying implementations\n// {consul, etcd, zookeeper, ...}.\ntype Registry interface {\n\tInit(...Option) error\n\tOptions() Options\n\tRegister(*Service, ...RegisterOption) error\n\tDeregister(*Service, ...DeregisterOption) error\n\tGetService(string, ...GetOption) ([]*Service, error)\n\tListServices(...ListOption) ([]*Service, error)\n\tWatch(...WatchOption) (Watcher, error)\n\tString() string\n}\n\ntype Service struct {\n\tName      string            `json:\"name\"`","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/registry/registry.go#L1-L30","documentation":"registry.ErrWatcherStopped is the sentinel returned by watcher Next() implementations (e.g. the memory watcher) when the watcher has been stopped. It lets callers distinguish a clean, expected shutdown of the watch stream from a real failure.","triggerScenarios":"Calling Next() on any watcher whose Stop() has been invoked; the memory watcher's exit channel closed; wrappers that translate internal stop conditions to the sentinel.","commonSituations":"Graceful shutdown flows where the consumer loop is still iterating when Stop() is called; tests that stop watchers to unblock Next (e.g. TestEnvvar_WatchNextNoOpsUntilStop); leaked watchers stopped by cleanup code.","solutions":["Compare the returned error to registry.ErrWatcherStopped and exit the watch loop cleanly instead of logging an error","Only call Stop() after all Next() consumers finished, or run Next in a goroutine that exits on this sentinel","Use the sentinel to implement idempotent shutdown logic"],"exampleFix":"// before\nr, err := w.Next()\nif err != nil { panic(err) }\n// after\nr, err := w.Next()\nif err != nil {\n  if errors.Is(err, registry.ErrWatcherStopped) { return nil } // expected\n  return err\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isWatcherStopped(err error) bool {\n  return errors.Is(err, registry.ErrWatcherStopped)\n}","tryCatchPattern":"r, err := w.Next()\nif errors.Is(err, registry.ErrWatcherStopped) {\n  return nil // expected during shutdown\n}\nif err != nil { return err }","preventionTips":["Always compare against the registry.ErrWatcherStopped sentinel","Design watch loops to exit on the sentinel without logging errors","Ensure Stop() is idempotent and called exactly once per watcher"],"tags":["watcher","registry","lifecycle","sentinel-error"],"backgroundTag":"watcher-stopped","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}