microsoft/typescript-go · error

error watching %s: %w

Error message

error watching %s: %w

What it means

Synchronous error from WatchDirectory on the kqueue backend: the initial open of the watched root directory failed. subscribe() walks the tree first, then calls openForEvents (unix.Open with O_EVTONLY on darwin, O_RDONLY on other BSDs) per entry; a child-entry failure only drops that entry, but failure on the root aborts and returns this error. The %s is the directory path and the %w carries the underlying syscall errno, so errors.Is works against syscall values.

Source

Thrown at internal/fswatch/kqueue.go:484

		return nil
	}); err != nil {
		return err
	}

	// Open fds, register kevents, and publish subscriptions under b.mu.
	// Holding the lock for the entire block ensures that the event loop
	// cannot see a partially-built entries map, and that fds are always
	// tracked in fdToEntry (no leak on early return).
	b.mu.Lock()
	defer b.mu.Unlock()

	for path, entry := range entries {
		fd, err := openForEvents(entry.watchPath)
		if err != nil {
			if path == w.dir {
				b.cleanupEntriesLocked(entries)
				return &dirWatchError{
					err:      fmt.Errorf("error watching %s: %w", w.dir, err),
					dirWatch: w,
				}
			}
			delete(entries, path)
			continue
		}
		var ev unix.Kevent_t
		unix.SetKevent(&ev, fd, unix.EVFILT_VNODE, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE)
		ev.Fflags = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_EXTEND |
			unix.NOTE_ATTRIB | unix.NOTE_RENAME | unix.NOTE_REVOKE
		if _, err := unix.Kevent(b.kq, []unix.Kevent_t{ev}, nil, nil); err != nil {
			unix.Close(fd)
			if path == w.dir {
				b.cleanupEntriesLocked(entries)
				return &dirWatchError{
					err:      fmt.Errorf("error watching %s: %w", w.dir, err),
					dirWatch: w,
				}

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Verify the directory exists and is readable (unix.Access(dir, unix.R_OK)) immediately before subscribing
  2. Fix ownership/permissions of the target, or run the watcher with sufficient privileges
  3. Retry WatchDirectory with backoff when a create/delete race is possible
  4. Watch a stable parent directory and filter events if the target directory is short-lived

Example fix

// before
watch, err := fswatch.Kqueue().WatchDirectory(dir, cb)
if err != nil {
    return err // gives up on a transient race
}

// after
var watch fswatch.Watch
for i := 0; i < 5; i++ {
    var err error
    watch, err = fswatch.Kqueue().WatchDirectory(dir, cb)
    if err == nil {
        break
    }
    if !errors.Is(err, syscall.ENOENT) && !errors.Is(err, syscall.EACCES) {
        return err
    }
    time.Sleep(100 * time.Millisecond)
}
Defensive patterns

Strategy: retry

Validate before calling

if _, err := os.Stat(dir); err != nil {
    return err
}
if err := unix.Access(dir, unix.R_OK); err != nil {
    return fmt.Errorf("directory not readable: %w", err)
}

Try / catch

watch, err := fswatch.Kqueue().WatchDirectory(dir, cb)
if err != nil {
    switch {
    case errors.Is(err, syscall.ENOENT):
        // directory vanished mid-subscribe: recreate/retry
    case errors.Is(err, syscall.EACCES):
        // fix permissions or privileges
    default:
        return err
    }
}

Prevention

When it happens

Trigger: The directory is deleted between the tree walk and the open (race). The process lacks read permission on the directory (EACCES). The path sits on a hung or unmounted network mount (EIO/ESTALE). A macOS sandbox/Seatbelt policy denies the open.

Common situations: Caller checks os.Stat then subscribes while the directory is removed in between. CI jobs running unprivileged watch system paths such as /run or /var. NFS mounts that went away between discovery and watch.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/523d1bbaa98ad4d4. Report an issue: GitHub.