micro/go-micro · info

watcher stopped

Error message

watcher stopped

What it means

The in-memory registry watcher returns this when Next() is called after the watcher has been stopped: the exit channel is closed, so the select falls into the <-m.exit branch and returns 'watcher stopped' instead of a result.

Source

Thrown at registry/memory_watcher.go:23

)

type memWatcher struct {
	wo   WatchOptions
	res  chan *Result
	exit chan bool
	id   string
}

func (m *memWatcher) Next() (*Result, error) {
	for {
		select {
		case r := <-m.res:
			if len(m.wo.Service) > 0 && m.wo.Service != r.Service.Name {
				continue
			}
			return r, nil
		case <-m.exit:
			return nil, errors.New("watcher stopped")
		}
	}
}

func (m *memWatcher) Stop() {
	select {
	case <-m.exit:
		return
	default:
		close(m.exit)
	}
}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Treat the error as a normal shutdown signal: compare with registry.ErrWatcherStopped and exit the loop
  2. Ensure Stop() is called only after the Next() consumer loop has finished
  3. Run the consumer loop in a goroutine whose lifecycle is bounded by watcher Stop

Example fix

// before
for {
  r, err := w.Next()
  if err != nil { log.Fatal(err) } // treats normal stop as fatal
}
// after
for {
  r, err := w.Next()
  if err != nil {
    if err == registry.ErrWatcherStopped { return } // clean shutdown
    return err
  }
  handle(r)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// check before use
select {
case <-w.stopped:
  return // watcher already stopped; don't call Next
default:
}

Type guard

func isWatcherStopped(err error) bool {
  return errors.Is(err, registry.ErrWatcherStopped)
}

Try / catch

r, err := w.Next()
if isWatcherStopped(err) {
  return // clean shutdown, not a failure
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling memWatcher.Next() after memWatcher.Stop() (or after the owning registry was unsubscribed/stopped); racing a Next() loop against Stop() during shutdown.

Common situations: Tests that stop the registry/watcher while a consumer goroutine is still iterating; service shutdown sequences where watcher teardown happens before the event loop exits; memory registry used in unit tests with leaked watcher goroutines.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/bc9295b10897c33b. Report an issue: GitHub.