AdguardTeam/AdGuardHome · warning
watching %s %s: %w
Error message
watching %s %s: %w
What it means
Failed to register a filesystem watch path with the fsnotify watcher inside the TLS manager. The wrapped error is the underlying watcher.Add failure (typically OS-level inotify limits or a path that no longer exists). This is collected into an aggregate error list when the manager sets up watches on certificate/key paths.
Source
Thrown at internal/aghtls/defaultmanager.go:233
err := mgr.watcher.Remove(p)
if err != nil {
errs = append(errs, fmt.Errorf("unwatching %s %s: %w", what, p, err))
}
return errs
}
// appendWatchErr starts watching a file at path p described by what and
// appends an error to the errs slice, if any. Empty p is ignored.
func (mgr *DefaultManager) appendWatchErr(errs []error, what, p string) (result []error) {
if p == "" {
return errs
}
err := mgr.watcher.Add(p)
if err != nil {
errs = append(errs, fmt.Errorf("watching %s %s: %w", what, p, err))
}
return errs
}
// 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
}
}View on GitHub (pinned to b41aefbe51)
Solutions
- Verify the configured certificate and private key paths exist on disk at startup
- Raise the inotify watch limit: sysctl fs.inotify.max_user_watches=1048576
- If files are replaced atomically, ensure the watched path is a stable directory path rather than the transient file
- Inspect the wrapped error to distinguish ENOENT (missing path) from ENOSPC (watch limit)
Example fix
// before cert_path: /certs/live/example.com/fullchain.pem.new # renamed constantly // after cert_path: /certs/live/example.com/fullchain.pem
Defensive patterns
Strategy: validation
Validate before calling
for _, p := range []string{certPath, keyPath} {
if _, err := os.Stat(p); err != nil {
return fmt.Errorf("path %s unusable: %w", p, err)
}
} Try / catch
if err := mgr.LoadTLSConfig(ctx); err != nil {
// aggregate error; check strings.Contains for "watching" and the wrapped os error
log.Warn("cert watch setup failed; hot-reload disabled", "err", err)
} Prevention
- Pre-create certificate and key paths before manager initialization
- Monitor inotify usage in containers and raise limits via sysctl
- Watch stable directory paths when files are atomically replaced
When it happens
Trigger: Calling LoadTLSConfig/Refresh paths where the configured certificate or key path cannot be watched: path deleted or renamed after configuration, path is a non-existent directory, or the OS inotify watch limit (fs.inotify.max_user_watches) is exhausted.
Common situations: Cert-manager or acme.sh replaces certificate files atomically (rename/delete) between config load and watch registration; running in a container with a low inotify limit; misconfigured certificate path pointing to a missing file.
Related errors
- starting watcher: %w
- unwatching %s %s: %w
- shutting down watcher: %w
- reading cert file: %w
- reading key file: %w
AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27).
Data as JSON: /api/errors/09d1a658a04eea7b.
Report an issue: GitHub.