ClementTsang/bottom · error

missing state

Error message

missing state

What it means

After splitting the stat remainder into whitespace-separated fields, the parser takes the first field as the process state and requires at least one character from it (the state is a single letter like R, S, Z). An empty first field means there are no stat fields at all, so the library raises this error.

Solutions

  1. Retry reading the stat file once; truncation due to a dying process is usually transient.
  2. Skip the entry during enumeration, treating it as a process that disappeared mid-scan.
  3. Fix test fixtures to include a state letter immediately after the closing paren, e.g. '(bash) S 1 ...'.

Example fix

// before
let state = next_part(&mut rest)?.chars().next().unwrap();
// after
let proc = match Process::from_file(&stat_path) {
    Ok(p) => Some(p),
    Err(_) => None, // treat unreadable stat as process-gone
};
Defensive patterns

Strategy: validation

Validate before calling

fn has_state_field(line: &str) -> bool {
    line.rsplit_once(") ")
        .map(|(_, rest)| !rest.trim().is_empty())
        .unwrap_or(false)
}
if !has_state_field(&line) { skip_entry(); }

Type guard

fn first_field(rest: &str) -> Option<char> {
    rest.split(' ').next()?.chars().next()
}

Try / catch

match Process::from_file(path) {
    Err(e) if e.to_string().contains("missing state") => None,
    Err(e) => return Err(e.into()),
    Ok(p) => Some(p),
}

Prevention

When it happens

Trigger: Process::from_file is given a stat line whose post-comm remainder is empty or whitespace-only, so the state field cannot be extracted — typically a truncated read or a file containing only '(comm)' with nothing after it.

Common situations: Racing with process exit so the stat content is cut off after the comm field; malformed synthetic test input missing the state field; unusual procfs implementations that emit incomplete records.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at src/collection/processes/linux/process.rs:116

            // an rsplit over the full string.
            //
            // Sources/discussion:
            // - https://man.archlinux.org/man/proc_pid_stat.5.en
            // - https://stackoverflow.com/questions/23534263/what-is-the-maximum-allowed-limit-on-the-length-of-a-process-name#comment138697304_23534499
            // - https://elixir.bootlin.com/linux/v7.1.3/source/fs/proc/array.c#L100
            // - https://github.com/ClementTsang/bottom/pull/2163#issuecomment-5017857303
            let (comm, rest) = line[start_paren + 1..]
                .rsplit_once(") ")
                .ok_or_else(|| anyhow!("stat string is malformed"))?;

            (comm.to_string(), rest)
        };

        let mut rest = rest.split(' ');
        let state = next_part(&mut rest)?
            .chars()
            .next()
            .ok_or_else(|| anyhow!("missing state"))?;
        let ppid: Pid = next_part(&mut rest)?.parse()?;

        // Skip 4 fields (pgrp, session, tty_nr, tpgid)
        let mut rest = rest.skip(4);

        // read flags for kernel thread (PF_KTHREAD from include/linux/sched.h)
        let flags: u32 = next_part(&mut rest)?.parse()?;
        let is_kernel_thread: bool = flags & 0x00200000 != 0;

        // Skip 4 fields (minflt, cminflt, majflt, cmajflt)
        let mut rest = rest.skip(4);
        let utime: u64 = next_part(&mut rest)?.parse()?;
        let stime: u64 = next_part(&mut rest)?.parse()?;

        // cutime
        let _ = next_part(&mut rest)?;
        // cstime
        let _ = next_part(&mut rest)?;

View on GitHub (pinned to b77d317502)