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

TimedOut

TimedOut

Error message

process timed out after {:?}

What it means

output_with_timeout runs a child process while polling for completion; when the process exceeds the given timeout it is killed, pipe readers are dropped without joining (a surviving descendant may hold the inherited pipe), and an io::Error of kind TimedOut with 'process timed out after {timeout:?}' is returned. It is the generic bounded command runner used by completes_fast_command and related helpers.

Source

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

        std::thread::spawn(move || {
            let mut output = Vec::new();
            pipe.read_to_end(&mut output).map(|_| output)
        })
    });
    let deadline = Instant::now() + timeout;

    let status = loop {
        match child.try_wait() {
            Ok(Some(status)) => break status,
            Ok(None) => {
                if Instant::now() >= deadline {
                    let _ = child.kill();
                    let _ = child.wait();
                    // Do not join readers on the kill path: a surviving descendant may
                    // still hold an inherited pipe, and the timeout path must stay bounded.
                    drop(stdout_reader);
                    drop(stderr_reader);
                    return Err(io::Error::new(
                        io::ErrorKind::TimedOut,
                        format!("process timed out after {:?}", timeout),
                    ));
                }
                std::thread::sleep(Duration::from_millis(20));
            }
            Err(error) => {
                let _ = child.kill();
                let _ = child.wait();
                drop(stdout_reader);
                drop(stderr_reader);
                return Err(error);
            }
        }
    };

    let stdout = join_reader_with_deadline(stdout_reader, deadline)?;
    let stderr = join_reader_with_deadline(stderr_reader, deadline)?;

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Raise the timeout Duration to fit the command's worst-case runtime.
  2. Retry the command once with a larger budget if transient slowness is plausible.
  3. Investigate why the child hangs (stdin waiting? network? prompt?) and fix the invocation (e.g. pipe stdin, add non-interactive flags).
  4. Handle the TimedOut error explicitly and fall back to a default result instead of propagating.

Example fix

// before
let out = output_with_timeout(&mut cmd, Duration::from_secs(3))?;

// after
match output_with_timeout(&mut cmd, Duration::from_secs(10)) {
    Ok(out) => out,
    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
        eprintln!("command exceeded 10s, using default");
        default_output()
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Try / catch

match output_with_timeout(&mut cmd, timeout) {
    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
        eprintln!("command exceeded {:?}; using fallback", timeout);
        fallback()
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling output_with_timeout (or completes_fast_command) with a command that runs longer than the supplied Duration — e.g. a hung child, infinite-running command, or timeout set too short.

Common situations: Probing whether a command 'completes fast' when the command actually hangs; network-bound CLIs stalling; timeout budget miscalibrated for slow machines.

Understand the failure class

Related errors


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