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

  1. Reformat the value as FD:DEST, e.g. --verify-fd-mount 7:/tmp/socket-root
  2. Build the pair in one place with format!("{fd}:{dest}") so the separator cannot be lost
  3. 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
  4. 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

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


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/8447fb0595d3c8f0. Report an issue: GitHub.