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

process termination is only supported on Linux and Windows

Error message

process termination is only supported on Linux and Windows

What it means

The daemon takeover mechanism uses a platform-specific handle to terminate a predecessor daemon process. Process-kill support is implemented only for Linux and Windows; on other platforms Predecessor::open returns None (takeover declines) but if a handle is still used, signal() always fails with io::ErrorKind::Unsupported saying 'process termination is only supported on Linux and Windows'.

Source

Thrown at crates/codegen/xai-grok-workspace-daemon/src/daemonize.rs:567

    fn drop(&mut self) {
        use windows::Win32::Foundation::CloseHandle;
        // SAFETY: the handle is owned by self and closed exactly once.
        let _ = unsafe { CloseHandle(self.handle) };
    }
}

#[cfg(not(any(target_os = "linux", windows)))]
struct PredecessorTarget;

#[cfg(not(any(target_os = "linux", windows)))]
impl PredecessorTarget {
    /// Unsupported platform: never identify a predecessor (the takeover declines rather than kill blind).
    fn open(_pid: u32, _fragment: &str) -> Option<Self> {
        None
    }

    fn signal(&self, _forceful: bool) -> io::Result<()> {
        Err(io::Error::new(
            io::ErrorKind::Unsupported,
            "process termination is only supported on Linux and Windows",
        ))
    }
}

#[cfg(test)]
mod tests {
    // Used only by the linux-gated predecessor-takeover tests below.
    #[cfg(target_os = "linux")]
    use std::process::{Child, Command, Stdio};

    use tempfile::TempDir;

    use super::*;

    #[test]
    fn pidfile_acquire_is_exclusive() {

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Run the daemon takeover/kill path only on Linux or Windows.
  2. Add a cfg(any(target_os="linux", windows)) gate so the kill path is never invoked elsewhere.
  3. On unsupported platforms, decline takeover gracefully (treat like Predecessor::open returning None) instead of calling signal().

Example fix

// before
predecessor.signal(true)?;
// after
#[cfg(any(target_os = "linux", windows))]
predecessor.signal(true)?;
#[cfg(not(any(target_os = "linux", windows)))]
return Ok(()); // decline takeover rather than kill
Defensive patterns

Strategy: fallback

Validate before calling

fn kill_supported() -> bool {
    cfg!(any(target_os = "linux", windows))
}
// skip predecessor-kill flow when false

Try / catch

match predecessor.signal(forceful) {
    Ok(()) => {}
    Err(e) if e.kind() == std::io::ErrorKind::Unsupported => {
        // decline takeover instead of failing
        return decline_takeover();
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling signal() (forceful or graceful) on the predecessor-process handle on a platform other than Linux or Windows — e.g. macOS with a stubbed handle, or other Unix flavors where the kill implementation is absent.

Common situations: Running the workspace daemon on macOS or a non-Linux Unix where the SIGTERM/SIGKILL path was not ported; porting the daemon to a new platform without implementing the kill handle.

Related errors


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