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
- Fix the spawn path so the child is created with a new process group and its real leader pid (> 1) is captured
- Reject or log pid 0/1 at the source where the pid is obtained instead of enrolling it
- 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
- Always spawn children via setpgid/setsid helpers so the leader pid is real and > 1
- Never default the pid to 0 when a spawn fails — surface the spawn error instead
- Log a warning if the pid captured from the child is 0 or 1
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
- refusing to killpg the caller's own process group ({pid})
- process-group id {pid} exceeds i32::MAX; cannot be used with
- git {shown} failed in {}: {}
- failed to exec {program}: {err}
- hook JSON alias validation failed: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/46ed456e95b58aa9.
Report an issue: GitHub.