ClementTsang/bottom · error

Unexpected 'ps' output

Error message

Unexpected 'ps' output

What it means

backup_proc_cpu parses the output of a `ps` command (pid + cpu% pairs) on macOS. This panic fires when `ps` emits a line whose whitespace-split tokens do not pair up into even chunks of 2, or when a token count is odd, meaning `ps` produced output in an unexpected format.

Solutions

  1. Check what `ps -o pid= -o pcpu=` (or the exact command used) prints on the affected machine and confirm it emits clean two-column rows
  2. Ensure the standard macOS/BSD ps is installed and first in PATH
  3. Clear locale env vars (LC_ALL=C) that could change ps output format
  4. Report/patch the parser to skip header lines instead of panicking on malformed chunks

Example fix

// before
.for_each(|chunk| {
    let chunk: Vec<&str> = chunk.collect();
    if chunk.len() != 2 {
        panic!("Unexpected 'ps' output");
    }
// after
.for_each(|chunk| {
    let chunk: Vec<&str> = chunk.collect();
    if chunk.len() != 2 {
        eprintln!("skipping malformed ps output: {:?}", chunk);
        return;
    }
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: verify ps output shape before parsing
let out = Command::new("ps").args(["-o", "pid=,pcpu="]).output()?;
let ok = String::from_utf8_lossy(&out.stdout).lines().all(|l| l.split_whitespace().count() == 2);

Try / catch

// This is a panic, not a Result: catch via catch_unwind or pre-validate
let r = std::panic::catch_unwind(|| backup_proc_cpu());
match r { Ok(v) => v, Err(_) => default_map() }

Prevention

When it happens

Trigger: Calling process CPU collection when the `ps` binary on the system prints extra header lines, localized output, truncated/odd token counts, or any output where split_whitespace().chunks(2) yields a chunk with fewer than 2 tokens.

Common situations: Non-standard or busyboxed `ps` implementations, locale/HTML output options unsupported by the installed ps version, or `ps` emitting warning lines interleaved with data rows.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07). Data as JSON: /api/errors/9500aa18a943e4a7. Report an issue: GitHub.

Appendix: source

Thrown at src/collection/processes/macos.rs:38

    fn backup_proc_cpu(pids: &[Pid]) -> io::Result<IntHashMap<Pid, f32>> {
        let output = Command::new("ps")
            .args(["-o", "pid=,pcpu=", "-p"])
            .arg(
                // Has to look like this since otherwise, it you hit a `unstable_name_collisions`
                // warning.
                Itertools::intersperse(pids.iter().map(i32::to_string), ",".to_string())
                    .collect::<String>(),
            )
            .output()?;
        let mut result = IntHashMap::default();
        String::from_utf8_lossy(&output.stdout)
            .split_whitespace()
            .chunks(2)
            .into_iter()
            .for_each(|chunk| {
                let chunk: Vec<&str> = chunk.collect();
                if chunk.len() != 2 {
                    panic!("Unexpected 'ps' output");
                }
                let pid = chunk[0].parse();
                let usage = chunk[1].parse();
                if let (Ok(pid), Ok(usage)) = (pid, usage) {
                    result.insert(pid, usage);
                }
            });
        Ok(result)
    }

    fn parent_pid(process_val: &sysinfo::Process) -> Option<Pid> {
        process_val
            .parent()
            .map(|p| p.as_u32() as _)
            .or_else(|| fallback_macos_ppid(process_val.pid().as_u32() as _))
    }
}

View on GitHub (pinned to b77d317502)