openai/codex · critical

invalid bundled bubblewrap fd path: {err}

Error message

invalid bundled bubblewrap fd path: {err}

What it means

After digest verification, exec() runs bwrap through its /proc/self/fd/{fd} path so the exact verified file (not a re-resolvable path) is executed. That generated path is converted to a CString; the panic fires only if the string contains an interior NUL byte, which is impossible for the literal prefix /proc/self/fd/ followed by a decimal fd number. It is a defensive assertion guarding an effectively unreachable condition.

Source

Thrown at codex-rs/linux-sandbox/src/bundled_bwrap.rs:52

impl BundledBwrapLauncher {
    pub(crate) fn exec(&self, argv: Vec<String>, preserved_files: Vec<File>) -> ! {
        let bwrap_file = File::open(self.program.as_path()).unwrap_or_else(|err| {
            panic!(
                "failed to open bundled bubblewrap {}: {err}",
                self.program.as_path().display()
            )
        });
        if let Err(err) = verify_digest(&bwrap_file, expected_sha256(), self.program.as_path()) {
            eprintln!("{err}");
            std::process::exit(crate::BUNDLED_BWRAP_DIGEST_VERIFICATION_FAILURE_EXIT_CODE);
        }

        make_files_inheritable(&preserved_files);

        let fd_path = format!("/proc/self/fd/{}", bwrap_file.as_raw_fd());
        let program_cstring = CString::new(fd_path.as_str())
            .unwrap_or_else(|err| panic!("invalid bundled bubblewrap fd path: {err}"));
        let cstrings = argv_to_cstrings(&argv);
        let mut argv_ptrs: Vec<*const c_char> = cstrings
            .iter()
            .map(CString::as_c_str)
            .map(CStr::as_ptr)
            .collect();
        argv_ptrs.push(std::ptr::null());

        // SAFETY: `program_cstring` and every entry in `argv_ptrs` are valid C
        // strings for the duration of the call. On success `execv` does not return.
        unsafe {
            libc::execv(program_cstring.as_ptr(), argv_ptrs.as_ptr());
        }
        let err = std::io::Error::last_os_error();
        panic!(
            "failed to exec bundled bubblewrap {} via {fd_path}: {err}",
            self.program.as_path().display()
        );

View on GitHub (pinned to 339751715c)

Solutions

  1. Treat as an environment or binary integrity failure: rebuild from clean sources and re-verify the installation.
  2. Report upstream with the exact panic message and platform details; no correct caller code can trigger this path.
Defensive patterns

Strategy: validation

Validate before calling

// Defensive: reject paths with interior NUL bytes before any exec-style API
fn cstring_safe(s: &str) -> bool {
    !s.contains('\u{0}')
}
assert!(cstring_safe(program_path.to_str().unwrap_or("")));

Type guard

fn is_cstring_safe(s: &str) -> bool { !s.contains('\u{0}') }

Prevention

When it happens

Trigger: Not reachable through normal API use: the fd path is synthesized from a raw descriptor number. A hit indicates memory corruption, a patched/incompatible runtime, or a corrupted build rather than caller error.

Common situations: None in practice; would only surface under fuzzing, binary corruption, or a runtime tampering with format!/CString internals.

Related errors


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