jlcodes99/cockpit-tools · error · std::io::Error

Other

Other

Error message

process output reader panicked

What it means

join_reader_with_deadline joins the stdout/stderr reader thread of output_with_timeout. If the reader thread panicked (handle.join() returns Err), the panic is converted into an io::Error of kind Other with the message 'process output reader panicked'. This indicates a bug or unexpected condition inside the output-reading code, not a process-level failure.

Source

Thrown at src-tauri/src/modules/process_timeout.rs:89

        status,
        stdout,
        stderr,
    })
}

fn join_reader_with_deadline(
    reader: Option<JoinHandle<io::Result<Vec<u8>>>>,
    deadline: Instant,
) -> io::Result<Vec<u8>> {
    let Some(handle) = reader else {
        return Ok(Vec::new());
    };
    loop {
        if handle.is_finished() {
            return handle
                .join()
                .map_err(|_| {
                    io::Error::new(io::ErrorKind::Other, "process output reader panicked")
                })?
                .map_err(|error| error);
        }
        if Instant::now() >= deadline {
            // Detach the reader thread; it will exit when the pipe is eventually closed.
            drop(handle);
            return Err(io::Error::new(
                io::ErrorKind::TimedOut,
                "process output drain timed out",
            ));
        }
        std::thread::sleep(Duration::from_millis(20));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Report/inspect the reader thread panic — check for unwrap/expect inside output-reading code and make it error-returning instead of panicking.
  2. Retry the command once; if reproducible, it indicates a code defect that must be fixed upstream.
  3. As a caller, match on this error kind (Other with this message) and degrade gracefully rather than crashing.
  4. Update the crate to a version where the reader thread is panic-free.

Example fix

// before
let out = output_with_timeout(&mut cmd, timeout)?; // panics propagate here as Err(Other)

// after
let out = match output_with_timeout(&mut cmd, timeout) {
    Ok(o) => o,
    Err(e) if e.to_string() == "process output reader panicked" => {
        log::error!("reader thread bug; retrying");
        output_with_timeout(&mut cmd, timeout)?
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: try-catch

Try / catch

match output_with_timeout(&mut cmd, timeout) {
    Err(e) if e.to_string() == "process output reader panicked" => {
        log::error!("internal reader panic; retry once or report bug");
        output_with_timeout(&mut cmd, timeout)
    }
    other => other,
}

Prevention

When it happens

Trigger: The reader thread spawned by output_with_timeout panics while reading child output — e.g. an unwrap/expect inside the reader failing on malformed pipe state — and the parent then joins the thread within its deadline.

Common situations: Unexpected OS pipe errors combined with non-defensive reader code; library-version regressions in pipe handling; extremely large or rapidly closing output streams.

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/7a236129533b0002. Report an issue: GitHub.