{"record":{"id":"8a03a205c25aa5b3","repo":"microsoft/typescript-go","slug":"w-w-8a03a2","errorCode":null,"errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fswatch/fanotify_linux.go","lineNumber":374,"sourceCode":"\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\n\n\tfor {\n\t\tn, err := unix.Read(b.fanotifyFD, buf)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EWOULDBLOCK) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"Error reading from fanotify: %w\", err)\n\t\t}\n\t\tif n == 0 {","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/fanotify_linux.go#L356-L392","documentation":"maybeWrapUnsupportedFilesystem upgrades fanotify syscall errors whose errno is EOPNOTSUPP, ENOTSUP, or ENODEV into a dual-wrapped error that also carries the sentinel fswatch.ErrFilesystemUnsupported ('fswatch: watcher backend unsupported on this filesystem'). This is the library's contract for 'this backend cannot watch THIS filesystem', letting callers branch on errors.Is and pick a different backend while the original errno stays inspectable.","triggerScenarios":"fanotify_mark or name_to_handle_at returning EOPNOTSUPP/ENOTSUP (FUSE/virtiofs/gRPC-FUSE mounts without file-handle support) or ENODEV (e.g. NTFS via fuseblk); any WatchDirectory on the fanotify backend against such a mount surfaces this wrapper.","commonSituations":"macOS Docker Desktop / Podman sharing code into Linux VMs; WSL2 /drv mounts; CI on FUSE-backed checkouts.","solutions":["Branch on errors.Is(err, fswatch.ErrFilesystemUnsupported) and retry the subscription with fswatch.Inotify() or a polling watcher","Move the working tree to a native Linux filesystem when inotify fanout cost matters","Keep the raw errno (errors.Is/errno extraction) for diagnostics when reporting upstream"],"exampleFix":"// before: treat any error as fatal\nw, err := fswatch.Default().WatchDirectory(dir, cb)\nif err != nil { log.Fatal(err) }\n\n// after\nw, err := fswatch.Default().WatchDirectory(dir, cb)\nif err != nil {\n    if errors.Is(err, fswatch.ErrFilesystemUnsupported) {\n        w, err = fswatch.Inotify().WatchDirectory(dir, cb)\n    }\n    if err != nil { log.Fatal(err) }\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func isUnsupportedFS(err error) bool {\n    return err != nil && errors.Is(err, fswatch.ErrFilesystemUnsupported)\n}","tryCatchPattern":"w, err := fswatch.Default().WatchDirectory(dir, cb, opts...)\nif err != nil {\n    if errors.Is(err, fswatch.ErrFilesystemUnsupported) {\n        // Errno stays wrapped for diagnostics; switch backend for this path.\n        log.Printf(\"fanotify unsupported on %s (%v); using inotify\", dir, err)\n        w, err = fswatch.Inotify().WatchDirectory(dir, cb, opts...)\n    }\n    if err != nil { return err }\n}","preventionTips":["Make ErrFilesystemUnsupported part of your startup matrix: decide backends per mount, not per process","Keep the inner errno when logging (errors.Is works through both wraps) so reports stay actionable","Integration-test your fallback path on CI runners that actually use FUSE mounts"],"tags":["linux","fanotify","errors-is","fallback","fswatch"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}