rust-lang/rust · error · io::Error
pidfd_spawnp succeeded but the child's PID could not be…
Error message
pidfd_spawnp succeeded but the child's PID could not be obtained
What it means
Returned by Rust std's Linux pidfd-spawn path after pidfd_spawnp succeeds but the subsequent pidfd.pid() call fails. The child has already been spawned and std holds its pidfd, yet it cannot read the PID - typically because the PID-fetching ioctl is unsupported, glibc's procfs fallback is blocked, or the process is out of file descriptors. The original error kind is preserved and wrapped with this explanatory message.
Solutions
- Disable create_pidfd on the Command to fall back to fork+exec, which returns the PID directly.
- Ensure procfs is mounted and readable at /proc in the sandbox/container.
- Loosen the seccomp filter to allow the pidfd-related ioctls, or raise RLIMIT_NOFILE.
- Upgrade glibc to a version with a correct pidfd pid retrieval implementation.
Example fix
// before
let mut cmd = Command::new("./child");
cmd.create_pidfd(true);
let child = cmd.spawn()?;
// after
let mut cmd = Command::new("./child");
// do not request a pidfd; rely on fork+exec
let child = cmd.spawn()?; Defensive patterns
Strategy: fallback
Validate before calling
// Disable pidfd when running in restricted sandboxes.
fn wants_pidfd() -> bool {
std::env::var_os("SANDBOX").is_none() && std::fs::metadata("/proc/self").is_ok()
}
// if !wants_pidfd() { cmd.create_pidfd(false); } Try / catch
let mut cmd = Command::new(prog);
cmd.create_pidfd(true);
match cmd.spawn() {
Ok(child) => Ok(child),
Err(e) if e.to_string().contains("PID could not be obtained") => {
let mut cmd2 = Command::new(prog); // no pidfd
cmd2.spawn()
}
Err(e) => Err(e),
} Prevention
- Do not request pidfd in containers that mask /proc or restrict ioctls.
- Ensure procfs is mounted and RLIMIT_NOFILE is adequate.
- Fall back to fork+exec by disabling create_pidfd on spawn errors.
When it happens
Trigger: Spawning a child with Command and .create_pidfd(true) on a Linux kernel/glibc combination where pidfd_spawnp works but pidfd_getfd/pid retrieval does not - e.g. restricted seccomp filter blocking /proc, procfs unmounted, or EMFILE (out of fds). The fallback at PIDFD_SUPPORTED already ruled out ENOSYS, so this is a post-spawn inconsistency.
Common situations: Containerized runtime with procfs masked/read-only; seccomp profile missing the ioctl syscalls; very high fd usage hitting RLIMIT_NOFILE; older glibc advertising pidfd_spawnp but buggy pid query.
Related errors
- failed to open NUL device for child
- process groups are not supported on espidf
- creating or truncating a file requires write or append…
- creating or truncating a file requires write or append…
- Failed to convert address to SocketAddr
AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10).
Data as JSON: /api/errors/d1aada0d49fb82b6.
Report an issue: GitHub.
Appendix: source
Thrown at library/std/src/sys/process/unix/unix.rs:810
&& e.raw_os_error() == Some(libc::ENOSYS)
{
PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed);
return Ok(None);
}
spawn_res?;
use crate::os::fd::{FromRawFd, IntoRawFd};
let pidfd = PidFd::from_raw_fd(pidfd);
let pid = match pidfd.pid() {
Ok(pid) => pid,
Err(e) => {
// The child has been spawned and we are holding its pidfd.
// But we cannot obtain its pid even though pidfd_spawnp and getpid support
// was verified earlier.
// This is quite unlikely, but might happen if the ioctl is not supported,
// glibc tries to use procfs and we're out of file descriptors.
return Err(Error::new(
e.kind(),
"pidfd_spawnp succeeded but the child's PID could not be obtained",
));
}
};
return Ok(Some(Process::new(pid as i32, pidfd.into_raw_fd())));
}
// Safety: -1 indicates we don't have a pidfd.
let mut p = Process::new(0, -1);
let spawn_res = spawn_fn(
&mut p.pid,
self.get_program_cstr().as_ptr(),
file_actions.0.as_ptr(),
attrs.0.as_ptr(),
self.get_argv().as_ptr() as *const _,View on GitHub (pinned to 7088e4b63a)