gohugoio/hugo · info
errNoSuchWatch
errNoSuchWatch
Error message
watch does not exist
What it means
errNoSuchWatch (watcher/filenotify/poller.go:19) is returned by the polling file watcher's Remove(name) when the given path isn't in its watches map. It indicates an attempt to unwatch a file/directory that was never added or was already removed, keeping Remove's contract symmetric with fsnotify's behavior.
Source
Thrown at watcher/filenotify/poller.go:19
package filenotify
import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors"
)
var (
// errPollerClosed is returned when the poller is closed
errPollerClosed = errors.New("poller is closed")
// errNoSuchWatch is returned when trying to remove a watch that doesn't exist
errNoSuchWatch = errors.New("watch does not exist")
)
// filePoller is used to poll files for changes, especially in cases where fsnotify
// can't be run (e.g. when inotify handles are exhausted)
// filePoller satisfies the FileWatcher interface
type filePoller struct {
// the duration between polls.
interval time.Duration
// watches is the list of files currently being polled, close the associated channel to stop the watch
watches map[string]struct{}
// Will be closed when done.
done chan struct{}
// events is the channel to listen to for watch events
events chan fsnotify.Event
// errors is the channel to listen to for watch errors
errors chan error
// mu locks the poller for modification
mu sync.MutexView on GitHub ↗ (pinned to 8a468df065)
Solutions
- Generally harmless — Hugo tolerates it during watch-set updates; ignore one-off occurrences.
- Restart `hugo server` if watching becomes inconsistent after heavy file churn.
- Avoid polling mode where possible (fix inotify limits instead of --poll) to reduce watch-set races.
Defensive patterns
Strategy: type-guard
Validate before calling
// track what you've added
if !watched[path] { return } // nothing to remove Type guard
func isNoSuchWatch(err error) bool { return err != nil && err.Error() == "watch does not exist" } Try / catch
if err := w.Remove(path); err != nil {
if isNoSuchWatch(err) { /* already removed — idempotent, ignore */ } else { return err }
} Prevention
- Keep a set of currently-watched paths and only Remove ones you added
- Treat Remove of a missing watch as idempotent no-op rather than a failure
- Avoid double-remove races by serializing add/remove on the watcher through one goroutine
When it happens
Trigger: filePoller.Remove(name) called for a path absent from w.watches — e.g. Hugo's server removing a watch for a deleted/renamed directory that was already unwatched, or double-removal during watcher reconfiguration.
Common situations: Rapid delete/rename churn of content or asset directories while `hugo server` runs in polling mode (--poll, network mounts, exhausted inotify); module/mount config changes that rebuild the watch set and remove paths twice.
Related errors
- errPollerClosed
- watch exists
- 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/d9b5033c73ed34d3.json.
Report an issue: GitHub ↗.