openai/codex · error · io::Error
InvalidInput
InvalidInput
Error message
descriptor-backed mount must contain FD:DEST: {mount} What it means
Thrown by verify_fd_mounts in codex-rs/linux-sandbox/src/fd_mount.rs, the trusted inner stage of the Codex Linux sandbox. Each --verify-fd-mount value must be a single FD:DEST pair; the function splits on the first colon via split_once and rejects the whole value with ErrorKind::InvalidInput when no colon is present, because it cannot identify which descriptor to authenticate. The check runs before any descriptor is adopted, so a malformed pair never reaches bubblewrap mount setup.
Source
Thrown at codex-rs/linux-sandbox/src/fd_mount.rs:17
//! Authenticate descriptor-backed mounts before sandboxed code can inherit them.
use std::collections::HashSet;
use std::fs;
use std::fs::File;
use std::io;
use std::os::fd::AsRawFd;
use std::os::fd::FromRawFd;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
pub(crate) fn verify_fd_mounts(mounts: &[String]) -> io::Result<()> {
let mut claimed_descriptors = HashSet::with_capacity(mounts.len());
for mount in mounts {
let (descriptor, destination) = mount.split_once(':').ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("descriptor-backed mount must contain FD:DEST: {mount}"),
)
})?;
let descriptor = descriptor.parse::<libc::c_int>().map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("descriptor-backed mount has an invalid descriptor: {mount}"),
)
})?;
if descriptor <= libc::STDERR_FILENO {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("descriptor-backed mount cannot use a standard descriptor: {descriptor}"),
));
}
if !claimed_descriptors.insert(descriptor) {
return Err(io::Error::new(View on GitHub (pinned to 339751715c)
Solutions
- Reformat the value as FD:DEST, e.g. --verify-fd-mount 7:/tmp/socket-root
- Build the pair in one place with format!("{fd}:{dest}") so the separator cannot be lost
- Drive the sandbox through the crate launcher (exec_bwrap) instead of invoking the inner stage by hand; it emits well-formed --verify-fd-mount pairs
- Add a CI assertion over the exact argv you assemble
Example fix
// before --verify-fd-mount /tmp/socket-root // after --verify-fd-mount 7:/tmp/socket-root
Defensive patterns
Strategy: validation
Validate before calling
fn fd_mount_has_pair(value: &str) -> bool {
value.split_once(':').is_some()
}
for m in &mounts {
if !fd_mount_has_pair(m) {
return Err(format!("mount spec missing FD:DEST separator: {m}"));
}
} Type guard
fn is_fd_mount_pair(value: &str) -> bool {
value.split_once(':').is_some()
} Try / catch
match verify_fd_mounts(&mounts) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
eprintln!("invalid fd mount spec: {e}");
std::process::exit(libc::EXIT_FAILURE);
}
Err(e) => return Err(e),
} Prevention
- Derive FD:DEST pairs with a single format! call instead of string surgery
- Smoke-test the sandbox argv in CI
- Never hand-edit --verify-fd-mount values
When it happens
Trigger: Running codex-linux-sandbox with --verify-fd-mount /tmp/socket-root (destination only), --verify-fd-mount 7tmp/dest, or any value where split_once(':') returns None. In practice this comes from a hand-built argv or launcher code that formats the pair without the ':' separator.
Common situations: Hand-testing the sandbox binary with edited flags; a code path that concatenates fd and destination without the colon; a templating bug that drops the fd field entirely.
Related errors
- PermissionDenied
- invalid legacy bubblewrap fd mount: {error}
- bubblewrap is unavailable: no system bwrap was found on PATH
- failed to open bundled bubblewrap {}: {err}
- failed to exec bundled bubblewrap {} via {fd_path}: {err}
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/8447fb0595d3c8f0.
Report an issue: GitHub.