xai-org/grok-build · error
Failed to capture ripgrep stdout
Error message
Failed to capture ripgrep stdout
What it means
Immediately after spawning ripgrep, content_search_streaming takes ownership of the child's stdout. If stdout is somehow absent (take() returns None), this error is thrown. In practice spawn() was configured with piped stdout, so this is a defensive check that should rarely fire.
Source
Thrown at crates/codegen/xai-grok-workspace/src/file_system/content.rs:151
params: &ContentSearchParams,
on_status: F,
) -> anyhow::Result<ContentSearchData>
where
F: Fn(ContentSearchBatch) + Send + 'static,
{
let max_files = params.max_files.unwrap_or(DEFAULT_MAX_FILES);
let max_matches = params.max_matches.unwrap_or(DEFAULT_MAX_MATCHES);
let mut cmd = build_ripgrep_command(root, params);
#[allow(clippy::disallowed_methods)] // waited on below; killed on drop (cancellation)
let mut child = cmd
.spawn()
.map_err(|e| anyhow::anyhow!("Failed to spawn ripgrep: {}", e))?;
let stdout = child
.stdout
.take()
.ok_or_else(|| anyhow::anyhow!("Failed to capture ripgrep stdout"))?;
let mut reader = BufReader::new(stdout).lines();
let mut files: Vec<ContentMatchFile> = Vec::new();
let mut current_file: Option<ContentMatchFile> = None;
let mut total_matches = 0usize;
let mut pending_files: Vec<ContentMatchFile> = Vec::new();
let mut last_notify = Instant::now();
let mut hit_limit = false;
while let Ok(Some(line)) = reader.next_line().await {
if line.is_empty() {
continue;
}
let json: serde_json::Value = match serde_json::from_str(&line) {
Ok(v) => v,
Err(_) => continue,
};View on GitHub (pinned to bc7f02eddd)
Solutions
- Inspect build_ripgrep_command and ensure it sets `.stdout(Stdio::piped())`.
- Re-run — if transient (pipe creation failure), retrying usually works.
- Check sandbox/SELinux/seccomp policies that may block anonymous pipes for child processes.
Example fix
// before: stdout not piped
Command::new("rg").args(&args).spawn()
// after
Command::new("rg").args(&args)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn() Defensive patterns
Strategy: try-catch
Validate before calling
fn ripgrep_piped() -> bool {
std::process::Command::new("rg")
.arg("--version")
.stdout(std::process::Stdio::piped())
.output()
.map(|o| o.status.success())
.unwrap_or(false)
} Try / catch
match content_search_streaming(root, params).await {
Ok(m) => m,
Err(e) if e.to_string() == "Failed to capture ripgrep stdout" => {
eprintln!("internal error: ripgrep stdout not piped; check build_ripgrep_command");
fallback_search(root, params)
}
Err(e) => return Err(e),
} Prevention
- Never modify build_ripgrep_command to drop Stdio::piped() on stdout
- Add a unit test asserting the child's stdout is piped before reading
- Keep a non-streaming fallback search path for degraded environments
When it happens
Trigger: Calling content_search_streaming when the child process was spawned without Stdio::piped() for stdout (e.g. build_ripgrep_command changed or was overridden), or a platform anomaly where the pipe could not be created.
Common situations: A modification to build_ripgrep_command that switched stdout to Stdio::inherit() or null; unusual sandboxed environments that deny pipe creation; forks/wrappers of the command builder.
Related errors
- Failed to spawn ripgrep: {}
- spawn shell in {}: {e}
- Failed to set working directory to {:?}: {}
- Failed to load config: {e}
- failed to run {program}: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/f9f539e255fc3bdf.
Report an issue: GitHub.