xai-org/grok-build · error

command failed to start: {e}

Error message

command failed to start: {e}

What it means

run_capped spawns a helper auth provider command via tokio::process; if spawn() itself fails the error is wrapped as 'command failed to start'. This happens before any timeout/capture logic.

Source

Thrown at crates/codegen/xai-grok-shell/src/auth/auth_provider.rs:288

/// Spawn `cmd`, capture stdout/stderr with a byte cap (reading both
/// concurrently so a full pipe on one can't deadlock the other; a runaway helper
/// is drained to a sink past the cap so it can't wedge the wait), and bound the
/// whole run by `timeout`. Exceeding the stdout cap is an error.
///
/// On timeout the child's entire process group is killed. The helper is a group
/// leader (`detach_command`'s `setsid`), so a compound `sh -c` helper's
/// grandchildren -- and the `GROK_AUTH_PROVIDER_*` credentials in their env --
/// do not outlive the reported timeout; `kill_on_drop` alone would reap only the
/// direct child.
async fn run_capped(
    cmd: &mut tokio::process::Command,
    timeout: std::time::Duration,
) -> anyhow::Result<std::process::Output> {
    #[allow(clippy::disallowed_methods)] // killed at the timeout this call reports
    let mut child = cmd
        .spawn()
        .map_err(|e| anyhow::anyhow!("command failed to start: {e}"))?;
    // Enroll the child's process group so the timeout path can tear down the
    // whole tree. Best-effort: if enrollment fails, `kill_on_drop` still reaps
    // the direct child.
    let mut group = xai_grok_tools::util::ProcessGroup::new()
        .map_err(|e| anyhow::anyhow!("process group setup failed: {e}"))?;
    if let Err(e) = group.attach(&child) {
        tracing::debug!(error = %e, "auth provider: could not enroll helper process group");
    }
    let stdout = child.stdout.take().expect("stdout is piped");
    let stderr = child.stderr.take().expect("stderr is piped");
    let mut out_buf = Vec::new();
    let mut err_buf = Vec::new();

    // One extra stdout byte so an over-cap write is detectable, not truncated.
    // The stderr read is advisory (it only feeds the failure log), so only
    // stdout governs the mint.
    let capture = async {
        let (out_res, err_res) = tokio::join!(

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Verify the helper command exists and is executable: run it manually and check `which <cmd>`
  2. Fix the configured path/PATH so the binary is found
  3. If packaged, ensure the helper ships with the build/container image

Example fix

// before
provider_cmd = "~/tools/grok-auth-helper"; // not expanded, ENOENT
// after
provider_cmd = "/usr/local/bin/grok-auth-helper";
Defensive patterns

Strategy: validation

Validate before calling

use std::process::Command;
let ok = Command::new(cmd_path)
    .arg("--version")
    .output()
    .map(|o| o.status.success())
    .unwrap_or(false);
assert!(ok, "auth helper {cmd_path} missing or not executable");

Try / catch

match mint_provider_token().await {
    Err(e) if e.to_string().starts_with("command failed to start") => {
        eprintln!("auth helper missing: {e}; install it or fix PATH");
    }
    other => other?,
}

Prevention

When it happens

Trigger: mint_provider_token invoking a helper binary that does not exist, lacks execute permission, or whose interpreter is missing (ENOENT/EACCES).

Common situations: Provider CLI not installed or not on PATH after an environment change, wrong config path to the helper, missing shebang interpreter, or container images without the helper binary.

Related errors


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