AdguardTeam/AdGuardHome · error

starting watcher: %w

Error message

starting watcher: %w

What it means

Failed to start the underlying fsnotify watcher that monitors TLS certificate files for changes. The wrapped error comes from watcher.Start, usually an OS-level failure to initialize inotify/ReadDirectoryChangesW.

Source

Thrown at internal/aghtls/defaultmanager.go:257

// Refresh implements the [service.Refresher] interface for *DefaultManager.
func (mgr *DefaultManager) Refresh(ctx context.Context) (err error) {
	mgr.logger.DebugContext(ctx, "refreshing")

	select {
	case mgr.updates <- UpdateSignal{}:
		return nil
	case <-ctx.Done():
		return fmt.Errorf("refreshing: %w", ctx.Err())
	default:
		return nil
	}
}

// Start implements the [service.Interface] interface for *DefaultManager.
func (mgr *DefaultManager) Start(ctx context.Context) (err error) {
	err = mgr.watcher.Start(ctx)
	if err != nil {
		return fmt.Errorf("starting watcher: %w", err)
	}

	go mgr.handleEvents(ctx)
	go mgr.handleCertFileChange(ctx)

	return nil
}

// Shutdown implements the [service.Interface] interface for *DefaultManager.
func (mgr *DefaultManager) Shutdown(ctx context.Context) (err error) {
	defer close(mgr.updates)

	err = mgr.watcher.Shutdown(ctx)
	if err != nil {
		return fmt.Errorf("shutting down watcher: %w", err)
	}

	return nil

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Raise fs.inotify.max_user_instances if the OS reports too many inotify instances
  2. Check for fd leaks (lsof) if ENOMEM/EMFILE appears in the wrapped error
  3. Ensure Start is called exactly once per manager lifecycle
Defensive patterns

Strategy: try-catch

Validate before calling

if rlimitCheck() { /* ensure fds available */ }

Try / catch

if err := mgr.Start(ctx); err != nil {
    log.Fatal("tls manager failed to start", "err", err) // service cannot hot-reload certs
}

Prevention

When it happens

Trigger: Calling DefaultManager.Start(ctx) when the fsnotify backend cannot create an inotify instance (fd exhaustion, inotify instances limit fs.inotify.max_user_instances) or after the watcher was already started/shut down.

Common situations: Running in a resource-constrained container with a low max_user_instances limit; file-descriptor exhaustion from leaking watchers; calling Start twice on the same manager.

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/d1889297f7125548. Report an issue: GitHub.