gohugoio/hugo · warning
watch exists
Error message
watch exists
What it means
Returned by Hugo's polling-based file watcher (watcher/filenotify/poller.go, used when native fsnotify is unavailable, e.g. on NFS or some container mounts) when filePoller.Add is called with a path that is already registered in its watches map. It guards against spawning a duplicate polling goroutine for the same file.
Source
Thrown at watcher/filenotify/poller.go:64
defer w.mu.Unlock()
if w.closed {
return errPollerClosed
}
item, err := newItemToWatch(name)
if err != nil {
return err
}
if item.left.FileInfo == nil {
return os.ErrNotExist
}
if w.watches == nil {
w.watches = make(map[string]struct{})
}
if _, exists := w.watches[name]; exists {
return fmt.Errorf("watch exists")
}
w.watches[name] = struct{}{}
go w.watch(item)
return nil
}
// Remove stops and removes watch with the specified name
func (w *filePoller) Remove(name string) error {
w.mu.Lock()
defer w.mu.Unlock()
return w.remove(name)
}
func (w *filePoller) remove(name string) error {
if w.closed {
return errPollerClosed
}View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Check whether the path is already watched before calling Add, or treat this error as benign and ignore it (the watch is already active).
- Call Remove(name) before re-adding if you intend to reset the watch.
- Deduplicate the list of watch paths (mounts, symlink-resolved paths) before registering them.
- If seen from hugo server, review config mounts for entries that resolve to the same directory.
Example fix
// before
for _, p := range paths {
w.Add(p)
}
// after
seen := map[string]bool{}
for _, p := range paths {
if seen[p] {
continue
}
seen[p] = true
if err := w.Add(p); err != nil {
return err
}
} Defensive patterns
Strategy: validation
Validate before calling
// Track watched paths yourself before calling Add
if _, ok := watched[path]; ok {
return nil // already watching
}
if err := poller.Add(path); err == nil {
watched[path] = struct{}{}
} Try / catch
if err := poller.Add(path); err != nil {
if err.Error() == "watch exists" {
// benign: path already watched, safe to continue
} else {
return fmt.Errorf("add watch %q: %w", path, err)
}
} Prevention
- Keep a set of already-watched paths and check membership before calling Add
- Deduplicate the path list (filepath.Clean + map) before registering watches
- Treat the 'watch exists' error as idempotent-success rather than fatal
- Remove the watch before re-adding when re-registering a path after config reload
When it happens
Trigger: Calling filePoller.Add(name) twice with the identical path string without an intervening Remove. In Hugo this happens when the server's watch setup registers the same directory/file more than once, e.g. overlapping mounts or module mounts resolving to the same path, while running with the poll-based watcher (--poll flag).
Common situations: Running `hugo server --poll` with module mounts or symlinks that map the same physical directory into the watch list twice; custom tools embedding hugolib that call Add on an already-watched path; retry logic re-adding a watch after a transient error without removing it first.
Related errors
- errPollerClosed
- errNoSuchWatch
- invalid value for flag poll: %s
- language %q not found
- unsupported format: %q
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/dd2299c32c6399ba.json.
Report an issue: GitHub ↗.