microsoft/typescript-go · error

statfs: %w

Error message

statfs: %w

What it means

The final setup step of arming a fanotify watch — statfs(2) to obtain the filesystem id used in the subscription key — failed. The mark and handle already succeeded, so this is almost always a race (directory unmounted or removed between syscalls) or a permission problem; the mark is rolled back before returning.

Source

Thrown at internal/fswatch/fanotify_linux.go:364

		return err
	}
	return nil
}

func (b *fanotifyBackend) markDir(w *dirWatch, path string, markPath string) error {
	if err := unix.FanotifyMark(b.fanotifyFD, fanotifyMarkAddFlags, b.markMask, unix.AT_FDCWD, markPath); err != nil {
		return maybeWrapUnsupportedFilesystem(err)
	}
	handle, _, err := unix.NameToHandleAt(unix.AT_FDCWD, markPath, 0)
	if err != nil {
		// Unmark since we can't track this directory without a handle.
		_ = unix.FanotifyMark(b.fanotifyFD, unix.FAN_MARK_REMOVE|unix.FAN_MARK_ONLYDIR, b.markMask, unix.AT_FDCWD, markPath)
		return maybeWrapUnsupportedFilesystem(fmt.Errorf("name_to_handle_at: %w", err))
	}
	var st unix.Statfs_t
	if err := unix.Statfs(markPath, &st); err != nil {
		_ = unix.FanotifyMark(b.fanotifyFD, unix.FAN_MARK_REMOVE|unix.FAN_MARK_ONLYDIR, b.markMask, unix.AT_FDCWD, markPath)
		return fmt.Errorf("statfs: %w", err)
	}
	key := makeFanotifyHandleKey(st.Fsid.Val, handle.Type(), handle.Bytes())
	sub := &fanotifySubscription{path: path, watchPath: markPath, dirWatch: w, key: key}
	b.subscriptions[key] = append(b.subscriptions[key], sub)
	return nil
}

func maybeWrapUnsupportedFilesystem(err error) error {
	if errors.Is(err, unix.EOPNOTSUPP) || errors.Is(err, unix.ENOTSUP) || errors.Is(err, unix.ENODEV) {
		return fmt.Errorf("%w: %w", err, ErrFilesystemUnsupported)
	}
	return err
}

// handleEvents reads and dispatches fanotify events from the fd.
func (b *fanotifyBackend) handleEvents() error {
	buf := b.readBuf
	watchersTouched := b.watchersTouched

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Verify the directory still exists (os.Stat) and re-check errno: ENOENT means race — recreate/re-subscribe when the directory reappears
  2. Subscribe to the parent directory first (to learn about recreation), then watch the target once it exists
  3. For EACCES on hardened systems, adjust LSM policy for the watching process

Example fix

// before
w, err := fswatch.Default().WatchDirectory(tmpBuildDir, cb) // statfs ENOENT: dir removed mid-setup

// after: ensure the dir exists, watch parent for recreation
os.MkdirAll(tmpBuildDir, 0o755)
w, err := fswatch.Default().WatchDirectory(tmpBuildDir, cb)
Defensive patterns

Strategy: retry

Validate before calling

// Avoid the TOCTOU window: verify the directory right before subscribing.
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
    os.MkdirAll(dir, 0o755) // recreate, or wait for the creator to finish
}
w, err := fswatch.Default().WatchDirectory(dir, cb)

Type guard

func isStatfsFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "statfs:")
}

Try / catch

w, err := fswatch.Default().WatchDirectory(dir, cb)
if err != nil && isStatfsFailure(err) {
    if _, serr := os.Stat(dir); serr == nil {
        // Directory exists: transient mount race — retry once.
        time.Sleep(50 * time.Millisecond)
        w, err = fswatch.Default().WatchDirectory(dir, cb)
    } else {
        // Directory is gone: watch its parent to catch recreation.
        w, err = fswatch.Default().WatchDirectory(filepath.Dir(dir), func(ev fswatch.Event) {
            if ev.HasCreate() && ev.Path == dir { resubscribe(dir, cb) }
        })
    }
}
if err != nil { return err }

Prevention

When it happens

Trigger: Watched directory deleted or unmounted between FanotifyMark/name_to_handle_at and statfs (TOCTOU); automount expiry; LSM (selinux) denying statfs on the target; NFS server dropping the export mid-setup.

Common situations: Temporary/build directories being cleaned while a watcher subscribes; tests creating and deleting fixtures rapidly; network mounts with flaky connectivity.

Related errors


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