{"record":{"id":"afac0b6b60e3c25c","repo":"microsoft/typescript-go","slug":"statfs-w","errorCode":null,"errorMessage":"statfs: %w","messagePattern":"statfs: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fswatch/fanotify_linux.go","lineNumber":364,"sourceCode":"\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (b *fanotifyBackend) markDir(w *dirWatch, path string, markPath string) error {\n\tif err := unix.FanotifyMark(b.fanotifyFD, fanotifyMarkAddFlags, b.markMask, unix.AT_FDCWD, markPath); err != nil {\n\t\treturn maybeWrapUnsupportedFilesystem(err)\n\t}\n\thandle, _, err := unix.NameToHandleAt(unix.AT_FDCWD, markPath, 0)\n\tif err != nil {\n\t\t// Unmark since we can't track this directory without a handle.\n\t\t_ = unix.FanotifyMark(b.fanotifyFD, unix.FAN_MARK_REMOVE|unix.FAN_MARK_ONLYDIR, b.markMask, unix.AT_FDCWD, markPath)\n\t\treturn maybeWrapUnsupportedFilesystem(fmt.Errorf(\"name_to_handle_at: %w\", err))\n\t}\n\tvar st unix.Statfs_t\n\tif err := unix.Statfs(markPath, &st); err != nil {\n\t\t_ = unix.FanotifyMark(b.fanotifyFD, unix.FAN_MARK_REMOVE|unix.FAN_MARK_ONLYDIR, b.markMask, unix.AT_FDCWD, markPath)\n\t\treturn fmt.Errorf(\"statfs: %w\", err)\n\t}\n\tkey := makeFanotifyHandleKey(st.Fsid.Val, handle.Type(), handle.Bytes())\n\tsub := &fanotifySubscription{path: path, watchPath: markPath, dirWatch: w, key: key}\n\tb.subscriptions[key] = append(b.subscriptions[key], sub)\n\treturn nil\n}\n\nfunc maybeWrapUnsupportedFilesystem(err error) error {\n\tif errors.Is(err, unix.EOPNOTSUPP) || errors.Is(err, unix.ENOTSUP) || errors.Is(err, unix.ENODEV) {\n\t\treturn fmt.Errorf(\"%w: %w\", err, ErrFilesystemUnsupported)\n\t}\n\treturn err\n}\n\n// handleEvents reads and dispatches fanotify events from the fd.\nfunc (b *fanotifyBackend) handleEvents() error {\n\tbuf := b.readBuf\n\twatchersTouched := b.watchersTouched","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/fanotify_linux.go#L346-L382","documentation":"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.","triggerScenarios":"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.","commonSituations":"Temporary/build directories being cleaned while a watcher subscribes; tests creating and deleting fixtures rapidly; network mounts with flaky connectivity.","solutions":["Verify the directory still exists (os.Stat) and re-check errno: ENOENT means race — recreate/re-subscribe when the directory reappears","Subscribe to the parent directory first (to learn about recreation), then watch the target once it exists","For EACCES on hardened systems, adjust LSM policy for the watching process"],"exampleFix":"// before\nw, err := fswatch.Default().WatchDirectory(tmpBuildDir, cb) // statfs ENOENT: dir removed mid-setup\n\n// after: ensure the dir exists, watch parent for recreation\nos.MkdirAll(tmpBuildDir, 0o755)\nw, err := fswatch.Default().WatchDirectory(tmpBuildDir, cb)","handlingStrategy":"retry","validationCode":"// Avoid the TOCTOU window: verify the directory right before subscribing.\nif info, err := os.Stat(dir); err != nil || !info.IsDir() {\n    os.MkdirAll(dir, 0o755) // recreate, or wait for the creator to finish\n}\nw, err := fswatch.Default().WatchDirectory(dir, cb)","typeGuard":"func isStatfsFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"statfs:\")\n}","tryCatchPattern":"w, err := fswatch.Default().WatchDirectory(dir, cb)\nif err != nil && isStatfsFailure(err) {\n    if _, serr := os.Stat(dir); serr == nil {\n        // Directory exists: transient mount race — retry once.\n        time.Sleep(50 * time.Millisecond)\n        w, err = fswatch.Default().WatchDirectory(dir, cb)\n    } else {\n        // Directory is gone: watch its parent to catch recreation.\n        w, err = fswatch.Default().WatchDirectory(filepath.Dir(dir), func(ev fswatch.Event) {\n            if ev.HasCreate() && ev.Path == dir { resubscribe(dir, cb) }\n        })\n    }\n}\nif err != nil { return err }","preventionTips":["Create target directories before subscribing; never race `rm -rf` of the watch root","For ephemeral build dirs, subscribe to the stable parent and (re)watch children on create events","One retry with a short delay resolves most statfs TOCTOU failures; more indicates an unmount"],"tags":["linux","fanotify","race-condition","statfs","fswatch"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}