{"record":{"id":"71824a3ebf023460","repo":"xai-org/grok-build","slug":"refusing-to-killpg-the-caller-s-own-process-group","errorCode":null,"errorMessage":"refusing to killpg the caller's own process group ({pid})","messagePattern":"refusing to killpg the caller's own process group \\((.+?)\\)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-tty-utils/src/lib.rs","lineNumber":632,"sourceCode":"    /// was never grouped, which would otherwise broadcast the kill.\n    pub fn new(pid: u32) -> io::Result<Self> {\n        if pid <= 1 {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                format!(\"refusing degenerate process-group id {pid} (0 = own group, 1 = init)\"),\n            ));\n        }\n        // killpg_unix casts `pid as i32`; values > i32::MAX wrap to negative,\n        // and killpg with a negative pgid returns EINVAL on Linux/macOS. Reject\n        // here so the invariant is safe-by-construction, not safe-by-OS-quirk.\n        if pid > i32::MAX as u32 {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                format!(\"process-group id {pid} exceeds i32::MAX; cannot be used with killpg\"),\n            ));\n        }\n        if i64::from(pid) == i64::from(nix::unistd::getpgrp().as_raw()) {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                format!(\"refusing to killpg the caller's own process group ({pid})\"),\n            ));\n        }\n        Ok(Self(pid))\n    }\n\n    /// The validated raw process-group id.\n    pub fn get(self) -> u32 {\n        self.0\n    }\n}\n\n/// Process-tree teardown handle.\n///\n/// - Unix: holds the validated group-leader id ([`ProcessGroupId`]); dispatches\n///   to `killpg(pgid, signal)`.\n/// - Windows: holds a Job Object with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`.","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-tty-utils/src/lib.rs#L614-L650","documentation":"Even when the pid is otherwise valid, ProcessGroup::new refuses a pid equal to the caller's current process group (getpgrp), because killpg on your own group would kill the caller itself. This is a self-preservation guard, reported as InvalidInput.","triggerScenarios":"Enrolling the caller's own pid or the pid of a process already sharing the caller's process group — e.g. passing std::process::id(), or a child that never actually moved to a new group (setpgid not called/failed).","commonSituations":"Calling the API from the top-level process instead of from the parent of a detached child; a spawn helper that silently failed to create the new group; testing with the current process's pid.","solutions":["Only enroll pids of children spawned into a dedicated process group (verify with the new_process_group / detach_* helpers)","Check the child actually left the caller's group: its pid must differ from the caller's pgid","If you have the caller's own pid, skip enrollment entirely rather than wrapping it"],"exampleFix":"// before\nlet pg = ProcessGroup::new(std::process::id())?; // refuses: own group\n// after\nlet child = spawn_detached(cmd)?; // child runs in its own process group\nlet pg = ProcessGroup::new(child.id())?;","handlingStrategy":"validation","validationCode":"fn not_own_group(pid: u32) -> bool {\n    i64::from(pid) != i64::from(nix::unistd::getpgrp().as_raw())\n}\nassert!(not_own_group(child_pid), \"cannot enroll caller's own process group\");","typeGuard":"fn not_own_group(pid: u32) -> bool {\n    i64::from(pid) != i64::from(nix::unistd::getpgrp().as_raw())\n}","tryCatchPattern":"if let Err(e) = ProcessGroup::new(pid) {\n    if e.kind() == io::ErrorKind::InvalidInput {\n        log::error!(\"pid {pid} is the caller's own process group; skipping kill enrollment\");\n        return Ok(()); // nothing to clean up\n    }\n    return Err(e.into());\n}","preventionTips":["Only enroll children spawned into a dedicated process group via detach/new_process_group helpers","Never pass std::process::id() (the caller's own pid) to enrollment","After a failed spawn, verify the child actually left the caller's group before enrolling"],"tags":["process","unix","killpg","safety","rust"],"backgroundTag":"invalid-process-group-id","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}