xai-org/grok-build · error · io::Error

refusing degenerate process-group id {pid} (0 = own group, 1

Error message

refusing degenerate process-group id {pid} (0 = own group, 1 = init)

What it means

ProcessGroup::new validates the pid it will later use with killpg. A process-group id of 0 means "the caller's own group" and 1 is init, so enrolling either would make a broadcast kill hit the wrong processes. The guard rejects pid <= 1 as InvalidInput so the invariant is safe by construction.

Source

Thrown at crates/codegen/xai-tty-utils/src/lib.rs:617

/// a standing guarantee that `killpg` can only ever reach a real, foreign
/// group — the highest-blast-radius primitive in process teardown is validated
/// once, at enrollment, rather than re-checked at each call site.
#[cfg(unix)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ProcessGroupId(u32);

#[cfg(unix)]
impl ProcessGroupId {
    /// Validate a group-leader pid. Errors for pid `0` (the caller's own
    /// group), pid `1` (init), or the caller's own process group (signalling it
    /// would kill this very process). A child spawned into its own group
    /// (`setpgid`/`setsid`, e.g. via [`new_process_group`] or a `detach_*`
    /// helper) always has a leader pid `> 1` distinct from the caller's pgid, so
    /// a well-formed enrollment never trips this — it only catches a child that
    /// was never grouped, which would otherwise broadcast the kill.
    pub fn new(pid: u32) -> io::Result<Self> {
        if pid <= 1 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("refusing degenerate process-group id {pid} (0 = own group, 1 = init)"),
            ));
        }
        // killpg_unix casts `pid as i32`; values > i32::MAX wrap to negative,
        // and killpg with a negative pgid returns EINVAL on Linux/macOS. Reject
        // here so the invariant is safe-by-construction, not safe-by-OS-quirk.
        if pid > i32::MAX as u32 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("process-group id {pid} exceeds i32::MAX; cannot be used with killpg"),
            ));
        }
        if i64::from(pid) == i64::from(nix::unistd::getpgrp().as_raw()) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("refusing to killpg the caller's own process group ({pid})"),
            ));

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Fix the spawn path so the child is created with a new process group and its real leader pid (> 1) is captured
  2. Reject or log pid 0/1 at the source where the pid is obtained instead of enrolling it
  3. Use the library's new_process_group / detach_* helpers, which guarantee a valid leader pid

Example fix

// before
let pg = ProcessGroup::new(child_pid)?; // child_pid == 0 after failed spawn
// after
if child_pid <= 1 {
    return Err(anyhow!("spawn failed: no valid process-group leader"));
}
let pg = ProcessGroup::new(child_pid)?;
Defensive patterns

Strategy: validation

Validate before calling

fn enrollable_pid(pid: u32) -> bool { pid > 1 }
if !enrollable_pid(child_pid) {
    return Err(anyhow!("spawn did not produce a valid process-group leader"));
}

Type guard

fn enrollable_pid(pid: u32) -> bool { pid > 1 }

Try / catch

match ProcessGroup::new(pid) {
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
        log::error!("child was never put in a new process group (pid={pid})");
        return Err(e.into());
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling ProcessGroup::new(0) or ProcessGroup::new(1), typically when a spawn helper returned a placeholder/invalid pid or the child was never actually put into a new process group (no setpgid/setsid).

Common situations: Propagating a default/zero pid after a failed spawn; recording the pid of a child that never got grouped; mocking code returning pid 1.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/46ed456e95b58aa9. Report an issue: GitHub.