microsoft/typescript-go · error
Error reading from fanotify: %w
Error message
Error reading from fanotify: %w
What it means
The fanotify event-dispatch loop read(2) from the fanotify fd failed with an errno other than EAGAIN/EWOULDBLOCK (both mean 'queue drained, stop'). Because the fd was opened O_NONBLOCK, a genuine read error means the fd is bad or arguments invalid — the backend terminates and the error propagates.
Source
Thrown at internal/fswatch/fanotify_linux.go:390
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
for {
n, err := unix.Read(b.fanotifyFD, buf)
if err != nil {
if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EWOULDBLOCK) {
break
}
return fmt.Errorf("Error reading from fanotify: %w", err)
}
if n == 0 {
break
}
metaSize := int(unsafe.Sizeof(unix.FanotifyEventMetadata{}))
data := buf[:n]
for len(data) >= metaSize {
meta := (*unix.FanotifyEventMetadata)(unsafe.Pointer(&data[0]))
if meta.Vers != unix.FANOTIFY_METADATA_VERSION {
return fmt.Errorf("unsupported fanotify metadata version: %d", meta.Vers)
}
eventLen := int(meta.Event_len)
if eventLen < int(meta.Metadata_len) || eventLen > len(data) {
break
}
// FID mode: fd should be FAN_NOFD, but close if somehow set.View on GitHub (pinned to 1bcfa18d79)
Solutions
- Ensure exactly one owner closes each watcher; never share Close across goroutines
- Treat the watcher as dead on this error: drop it and create a fresh subscription if watching must continue
- If EIO recurs, check dmesg and the health of the underlying disk/mount
Example fix
// before
go func() { w.Close() }()
go func() { w.Close() }() // second close races event loop read -> EBADF
// after: single-owner shutdown via sync.Once
var once sync.Once
shutdown = func() { once.Do(w.Close) } Defensive patterns
Strategy: retry
Type guard
func isFanotifyReadFailure(err error) bool {
return err != nil && strings.Contains(err.Error(), "Error reading from fanotify")
} Try / catch
err := watchUntilStopped(ctx, dir, cb)
if isFanotifyReadFailure(err) {
// Backend fd went bad; rebuild the watcher from scratch (fresh fds).
err = watchUntilStopped(ctx, dir, cb)
}
return err Prevention
- Single-owner Close per watcher; use sync.Once if multiple call sites may shut down
- Never call unix.Close on fds you obtained from the watcher internals
- Recurring EIO reads point to a failing disk: surface dmesg/filesystem health to the user
When it happens
Trigger: EBADF from the fanotify fd being closed by concurrent teardown (double Close, racing goroutines); EINVAL from a kernel/driver mismatch on buffer or flags; EIO from a failing filesystem underneath the watch.
Common situations: Multiple goroutines closing the same watcher; watchers stored in maps cleared during shutdown while events still flow; hardware/filesystem errors on the watched volume.
Related errors
- unable to open pipe: %w
- unable to poll: %w
- statfs: %w
- unable to initialize fanotify: %w
- fanotify_mark on '%s' failed: %w
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/ce78f0463c7b5896.
Report an issue: GitHub.