fish-shell/fish-shell · critical

eventfd

Error message

eventfd

What it means

FdMonitor::new creates its wakeup pipe via libc::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK) on platforms with eventfd support. If eventfd returns a negative fd (kernel too old, fd limit exhausted, or out of memory), fish prints perror("eventfd") and calls exit_without_destructors(1), aborting the process immediately.

Source

Thrown at src/fd_monitor.rs:49

/// [`try_consume()`](FdEventSignaller::try_consume) may be used to consume the event.
/// Importantly this is async signal safe. Of course it is `CLOEXEC` as well.
pub struct FdEventSignaller {
    // Always the read end of the fd; maybe the write end as well.
    fd: OwnedFd,
    #[cfg(not(have_eventfd))]
    write: OwnedFd,
}

impl FdEventSignaller {
    /// The default constructor will abort on failure (fd exhaustion).
    /// This should only be used during startup.
    pub fn new() -> Self {
        cfg_if! {
            if #[cfg(have_eventfd)] {
                // Note we do not want to use EFD_SEMAPHORE because we are binary (not counting) semaphore.
                let fd = unsafe { libc::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK) };
                if fd < 0 {
                    perror("eventfd");
                    exit_without_destructors(1);
                }
                use crate::fds::heightenize_fd;
                let Ok(fd) = heightenize_fd(unsafe { OwnedFd::from_raw_fd(fd) }, true) else {
                    perror("eventfd");
                    exit_without_destructors(1);
                };
                Self {
                    fd
                }
            } else {
                // Implementation using pipes.
                let Ok(pipes) = make_autoclose_pipes() else {
                    exit_without_destructors(1);
                };
                make_fd_nonblocking(pipes.read.as_raw_fd()).unwrap();
                make_fd_nonblocking(pipes.write.as_raw_fd()).unwrap();
                Self {

View on GitHub (pinned to edb719d76c)

Solutions

  1. Raise the file descriptor limit: `ulimit -n 65536` or adjust RLIMIT_NOFILE / systemd LimitNOFILE
  2. Check seccomp/container policy (Docker/Podman) allows the eventfd(2) syscall
  3. Confirm the kernel supports eventfd (CONFIG_EVENTFD=y); otherwise rebuild fish for a non-eventfd configuration
  4. Inspect `cat /proc/sys/fs/file-nr` for system-wide fd exhaustion
Defensive patterns

Strategy: retry

Validate before calling

# shell check before launching fish
if [ "$(cat /proc/sys/fs/file-nr | awk '{print $1}')" -gt "$(ulimit -n)" -o "$(ulimit -n)" -lt 1024 ]; then
  echo "fd limit too low / nearly exhausted" >&2
fi

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Startup or first use of FdMonitor when the eventfd(2) syscall fails: RLIMIT_NOFILE exhausted (EMFILE), ENFILE/EINTR persisting, or running on a kernel/container without eventfd support despite the build flag have_eventfd being enabled.

Common situations: Containers/seccomp profiles blocking eventfd, hitting the file-descriptor limit after many open files/sockets, very old kernels or minimal embedded systems where CONFIG_EVENTFD is disabled.


AI-assisted analysis of fish-shell/fish-shell@edb719d76c (2026-09-03). Data as JSON: /api/errors/0e965b88025b3466. Report an issue: GitHub.