{"record":{"id":"538db99f65dee294","repo":"microsoft/typescript-go","slug":"unable-to-open-pipe-w","errorCode":null,"errorMessage":"unable to open pipe: %w","messagePattern":"unable to open pipe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fswatch/fanotify_linux.go","lineNumber":223,"sourceCode":"// exercise the fallback path on kernels that natively support FAN_RENAME.\nfunc newFanotifyBackend(noRename bool) *fanotifyBackend {\n\tb := &fanotifyBackend{\n\t\tpipeFDs:         [2]int{-1, -1},\n\t\tfanotifyFD:      -1,\n\t\tnoRename:        noRename,\n\t\tsubscriptions:   map[fanotifyHandleKey][]*fanotifySubscription{},\n\t\tendedSignal:     make(chan struct{}),\n\t\treadBuf:         make([]byte, fanotifyBufferSize),\n\t\twatchersTouched: make(map[*dirWatch]struct{}),\n\t}\n\tb.pipeWriteFD.Store(-1)\n\tb.watcherBase.init(b)\n\treturn b\n}\n\nfunc (b *fanotifyBackend) start() error {\n\tif err := unix.Pipe2(b.pipeFDs[:], unix.O_CLOEXEC|unix.O_NONBLOCK); err != nil {\n\t\treturn fmt.Errorf(\"unable to open pipe: %w\", err)\n\t}\n\tb.pipeWriteFD.Store(int32(b.pipeFDs[1]))\n\tdefer func() {\n\t\tb.closeFDs()\n\t\tclose(b.endedSignal)\n\t}()\n\n\tfd, err := unix.FanotifyInit(fanotifyInitFlags, unix.O_RDONLY|unix.O_CLOEXEC)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to initialize fanotify: %w\", err)\n\t}\n\tb.fanotifyFD = fd\n\n\tpollfds := []unix.PollFd{\n\t\t{Fd: int32(b.pipeFDs[0]), Events: unix.POLLIN},\n\t\t{Fd: int32(b.fanotifyFD), Events: unix.POLLIN},\n\t}\n","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/fanotify_linux.go#L205-L241","documentation":"While starting the Linux fanotify backend, the wakeup pipe could not be created with pipe2(O_CLOEXEC|O_NONBLOCK). The raw errno is wrapped in the message. This is an OS-level failure of the backend's self-wakeup mechanism, before fanotify is even initialized.","triggerScenarios":"Process hitting the open-file-descriptor limit (EMFILE/ENFILE) at watcher start; kernel memory pressure causing ENOMEM; seccomp/container policies blocking the pipe2 syscall.","commonSituations":"Editors/servers creating many watchers or otherwise holding many fds (check ulimit -n); heavily sandboxed containers (gVisor, strict Docker seccomp profiles); fork-bombed or memory-starved CI runners.","solutions":["Raise the file-descriptor limit (ulimit -n / LimitNOFILE) and reduce concurrent open descriptors","Retry after freeing descriptors — EMFILE is transient if something leaks fds; find the leak","Relax the container seccomp/apparmor policy to allow pipe2","Use a non-fanotify backend (e.g. inotify via fswatch.Inotify()) or polling if the environment cannot support it"],"exampleFix":"// before\nw, err := fswatch.Default().WatchDirectory(dir, cb, fswatch.WithRecursive()) // fanotify start fails: pipe2 EMFILE\n\n// after: raise RLIMIT_NOFILE (or use a lighter backend)\nimport \"golang.org/x/sys/unix\"\nunix.Setrlimit(unix.RLIMIT_NOFILE, &unix.Rlimit{Cur: 65535, Max: 65535})\nw, err := fswatch.Inotify().WatchDirectory(dir, cb, fswatch.WithRecursive())","handlingStrategy":"fallback","validationCode":"// Before starting many watchers, confirm fd headroom.\nvar rl unix.Rlimit\nif err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rl); err == nil {\n    if rl.Cur < 1024 {\n        _ = unix.Setrlimit(unix.RLIMIT_NOFILE, &unix.Rlimit{Cur: 65535, Max: rl.Max})\n    }\n}","typeGuard":"func isPipeFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"unable to open pipe\")\n}","tryCatchPattern":"w, err := watcher.WatchDirectory(dir, cb, opts...)\nif err != nil && isPipeFailure(err) {\n    // EMFILE/ENOMEM at backend start: retry once, then degrade to polling.\n    time.Sleep(100 * time.Millisecond)\n    if w, err = watcher.WatchDirectory(dir, cb, opts...); err != nil {\n        return startPolling(dir, cb) // application-level poll fallback\n    }\n}","preventionTips":["Set RLIMIT_NOFILE explicitly in long-lived servers instead of trusting inherited limits","Cap the number of concurrent watchers and directory marks your app creates","Fix fd leaks (unclosed watches/files) found via lsof before they surface as pipe2 EMFILE"],"tags":["linux","fanotify","file-descriptors","fswatch","ulimit"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}