micro/go-micro · error

watcher is disabled

Error message

watcher is disabled

What it means

The memory loader supports disabling watchers via the WithWatcherDisabled loader option. When that option is set, Watch returns "watcher is disabled" immediately without creating a watcher. This is an intentional configuration guard, not a runtime failure — the application was explicitly configured not to support config watching.

Source

Thrown at config/loader/memory/memory.go:361

		if !m.opts.WithWatcherDisabled {
			go m.watch(idx, source)
		}
	}

	if err := m.reload(); err != nil {
		gerrors = append(gerrors, err.Error())
	}

	// Return errors
	if len(gerrors) != 0 {
		return errors.New(strings.Join(gerrors, "\n"))
	}
	return nil
}

func (m *memory) Watch(path ...string) (loader.Watcher, error) {
	if m.opts.WithWatcherDisabled {
		return nil, errors.New("watcher is disabled")
	}

	value, err := m.Get(path...)
	if err != nil {
		return nil, err
	}

	m.Lock()

	w := &watcher{
		exit:    make(chan bool),
		path:    path,
		value:   value,
		reader:  m.opts.Reader,
		updates: make(chan updateValue, 1),
	}
	w.version.Store(m.snap.Version)

View on GitHub (pinned to 24529f1404)

Solutions

  1. Remove the WithWatcherDisable option when constructing the loader if watching is desired.
  2. If disabling is intentional, guard the Watch call with a feature flag/config check and skip watching instead of erroring.
  3. Poll Get on an interval as a fallback where watchers are disabled.
  4. Check loader.Options() (or your wiring) to confirm whether watcher support is off before attempting Watch.

Example fix

// before
loader := memory.NewLoader(memory.WithWatcherDisable())
w, _ := loader.Watch()
// after
loader := memory.NewLoader() // watchable
w, err := loader.Watch()
if err != nil {
    log.Fatal(err)
}
Defensive patterns

Strategy: fallback

Validate before calling

if watcherDisabled {
    log.Println("config watching disabled; using polling")
    return nil
}
w, err := loader.Watch()
if err != nil {
    return err
}

Try / catch

w, err := loader.Watch()
if err != nil {
    if err.Error() == "watcher is disabled" {
        return startPoller(loader, interval)
    }
    return err
}

Prevention

When it happens

Trigger: Calling loader.Watch(path...) — or config.Watch which delegates to it — on a memory loader constructed with memory.WithWatcherDisable(), or with a config that passes that option through.

Common situations: Environments (serverless, batch jobs, tests) where dynamic config reload is intentionally disabled for performance, but shared library code unconditionally sets up a watcher; copying setup code that includes the disable option without realizing it blocks Watch.

Related errors


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