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
- Check the process for fd leaks or premature fd closure (ulimit -n, lsof) and fix the leak or raise the descriptor limit
- Run outside sandbox/seccomp policies that block fcntl, or add an allow rule for F_GETFD/F_SETFD
- 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
- Raise RLIMIT_NOFILE and fix fd leaks in the launching process
- Test plugin launching inside your sandbox/seccomp/container profile
- Never bypass by re-opening the path — the descriptor exists precisely to prove reviewed bytes
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
- MCP server ' ' has no command configured
- MCP server ' ' has no command configured
- A managed, project or plugin connector already uses this…
- Absolute path should not warn
- active plugin registry is missing its pre-dotenv…
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)