Hmbown/CodeWhale · error

failed to inherit reviewed plugin executable descriptor

Error message

failed to inherit reviewed plugin executable descriptor

What it means

On spawn, bind_file keeps the verified executable alive by passing it as an inherited file descriptor; it clears FD_CLOEXEC on the fd so the descriptor survives exec. This error is thrown when the fcntl calls to clear FD_CLOEXEC fail, so the verified descriptor cannot be handed to the child process. It aborts the launch rather than falling back to re-opening the path (which could have changed).

Solutions

  1. Check the process for fd leaks or premature fd closure (ulimit -n, lsof) and fix the leak or raise the descriptor limit
  2. Run outside sandbox/seccomp policies that block fcntl, or add an allow rule for F_GETFD/F_SETFD
  3. Report the failure with the OS/container details — if the launch path falls back to re-opening the file, that fallback must not bypass hash verification

Example fix

# before
$ ulimit -n  # 64, too low, fcntl fails on new fds
# after
$ ulimit -n 1024  # or fix fd leak in parent; retry launching the plugin
Defensive patterns

Strategy: try-catch

Validate before calling

# Before launch, confirm fd ops are permitted in this environment:
python3 - <<'EOF'
import fcntl, os
fd = os.open('/dev/null', os.O_RDONLY)
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags & ~fcntl.FD_CLOEXEC)
print('fcntl FD_CLOEXEC clearing works')
EOF

Try / catch

match launch_plugin() {
    Err(e) if e.to_string().contains("failed to inherit reviewed plugin executable descriptor") => {
        eprintln!("fd inheritance blocked (fd limit? sandbox/seccomp?). Not falling back to re-open: integrity would be lost.");
        std::process::exit(1);
    }
    other => other.expect("plugin launch"),
}

Prevention

When it happens

Trigger: bind_file (via prepare_stdio_launch or bind_command) bails when libc::fcntl(fd, F_GETFD) returns < 0 or F_SETFD clearing FD_CLOEXEC fails — i.e. the raw fd is invalid or the OS refused the descriptor-flag update.

Common situations: Exhausted or closed descriptors in the parent process; an unusual exec/parenting setup where the fd was closed before this point; container/OS security policies (seccomp, sandbox) blocking fcntl; platform-specific fd handling quirks.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/c575d9b3e173d45d. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp.rs:1041

            .iter()
            .map(|byte| format!("{byte:02x}"))
            .collect::<String>();
        if &actual != expected {
            anyhow::bail!("reviewed plugin executable bytes changed before spawn");
        }
        file.seek(std::io::SeekFrom::Start(0))
            .context("rewind reviewed launch file after verification")?;

        #[cfg(unix)]
        let launch_path = {
            use std::os::fd::AsRawFd as _;
            let fd = file.as_raw_fd();
            // SAFETY: `fd` is owned by `file`; clearing only FD_CLOEXEC keeps
            // that same descriptor available across the imminent exec.
            let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
            if flags < 0 || unsafe { libc::fcntl(fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC) } < 0
            {
                anyhow::bail!("failed to inherit reviewed plugin executable descriptor");
            }
            #[cfg(target_os = "linux")]
            let prefix = "/proc/self/fd";
            #[cfg(not(target_os = "linux"))]
            let prefix = "/dev/fd";
            std::ffi::OsString::from(format!("{prefix}/{fd}"))
        };

        #[cfg(not(unix))]
        let launch_path = path.as_os_str().to_os_string();

        self.opened_files.push(file);
        Ok(launch_path)
    }

    fn bind_cwd(&mut self, cwd: &Path) -> Result<()> {
        #[cfg(unix)]
        {

View on GitHub (pinned to 73e0f67d83)