vi/websocat · error

assertion failed 1425

Error message

assertion failed 1425

What it means

This panic comes from .expect("assertion failed 1425") on the child process stdout handle inside ProcessPeer::read. ProcessPeer keeps an Option<Child> whose stdout is only Some when the child was spawned with Stdio::piped(); the expect fires when stdout() returns None. It means the code tried to read from a child whose standard output was never captured or already taken.

Solutions

  1. When spawning the child, always set .stdout(Stdio::piped()) before creating the ProcessPeer.
  2. Ensure nothing else calls Child::stdout().take() on the same child before ProcessPeer reads it.
  3. Check the code that constructs ProcessPeer and make piped stdout a required precondition, failing fast at spawn time with a clear error instead of at read time.

Example fix

// before
let child = Command::new(prog).spawn()?;
let peer = ProcessPeer::new(child);
peer.read(buf)?; // panics: stdout not piped
// after
let child = Command::new(prog)
    .stdout(Stdio::piped())
    .spawn()?;
let peer = ProcessPeer::new(child);
peer.read(buf)?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_piped_stdout(cmd: &mut Command) -> &mut Command {
    cmd.stdout(Stdio::piped())
}
// call before spawn and before constructing ProcessPeer

Type guard

fn child_stdout_available(child: &Child) -> bool {
    // peek without taking: wrap child stdout in a field checked at construction
    child.stdout.is_some()
}

Try / catch

// panics cannot be caught in Rust (without catch_unwind); validate first
let peer = ProcessPeer::new(child);
match peer.try_read(buf) {
    Ok(n) => process(n),
    Err(e) => eprintln!("child stdout unavailable: {}", e),
}

Prevention

When it happens

Trigger: Creating a ProcessPeer for a child spawned without .stdout(Stdio::piped()), or after the child's stdout handle was already taken (take() on Child::stdout) and never put back.

Common situations: Configuring Command without piped stdout but assuming the peer wraps output; a spawn-time default (inherit/null) differing between environments or library versions; reading after another component (e.g. a logger) consumed the handle.

Related errors


AI-assisted analysis of vi/websocat@3a3574cd2f (2026-09-12). Data as JSON: /api/errors/f517588f0896232d. Report an issue: GitHub.

Appendix: source

Thrown at src/process_peer.rs:207

    exit_on_disconnect: bool,
}
#[derive(Clone)]
struct ProcessPeer {
    chld: Rc<RefCell<ForgetfulProcess>>,
    sighup_on_zero: bool,
    sighup_on_close: bool,
}

impl Read for ProcessPeer {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
        self.chld
            .borrow_mut()
            .chld
            .as_mut()
            .unwrap()
            .stdout()
            .as_mut()
            .expect("assertion failed 1425")
            .read(buf)
    }
}

impl Write for ProcessPeer {
    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
        #[cfg(unix)]
        {
            if self.sighup_on_zero && buf.is_empty() {
                // TODO use nix crate?
                if let Some(ref chld) = self.chld.borrow().chld {
                    unsafe {
                        extern crate libc;
                        libc::kill(chld.id() as libc::pid_t, libc::SIGHUP);
                    }
                }
            }
        }

View on GitHub (pinned to 3a3574cd2f)