sinelaw/fresh · error
Failed to wait for command
Error message
Failed to wait for command: {} What it means
After spawning the shell, run_shell_command_blocking calls child.wait() to collect the exit status. Failure to wait (the child handle's stdin/stdout pipes are broken or the process data is unreadable) yields this error wrapping the io::Error; note this is not the same as the command exiting non-zero.
Solutions
- Check the wrapped io::Error; BrokenPipe/ECHILD usually means the child died externally — treat it as a non-fatal command failure
- Re-run the command after restoring the terminal (the code re-enters the alternate screen afterward)
- Avoid killing the editor's process group while a shell command is running
- Consider using waitpid semantics/try_wait and reporting the exit status instead of erroring on reap failure
Example fix
// before
let status = child.wait()
.map_err(|e| anyhow::anyhow!("Failed to wait for command: {}", e))?;
// after
let status = child.wait().unwrap_or_else(|e| {
log::warn!("failed to reap shell command: {e}");
std::process::exit_status_approximation::failure()
}); Defensive patterns
Strategy: try-catch
Try / catch
// treat reap failure as a soft failure, not a fatal error
let status = match child.wait() {
Ok(s) => s,
Err(e) => {
log::warn!("could not reap shell command: {e}");
return Ok(1); // assume failure exit code
}
}; Prevention
- Don't kill the editor's process group while a synchronous shell command runs
- Keep the terminal session stable over ssh (use tmux/mosh for long commands)
- Handle ECHILD/BrokenPipe as 'command died externally' rather than propagating a hard error
- Always restore the TUI (alternate screen + raw mode) even when wait fails
When it happens
Trigger: child.wait() returns an io::Error — typically because the child process was reaped elsewhere, its pipes closed unexpectedly, or the process was killed in a way that invalidates the handle (SIGKILL of the process group, terminal teardown).
Common situations: User kills the shell's process group (Ctrl-C / kill -9) while a long command runs; terminal session drops over ssh mid-command; the process outlives the parent's ability to reap it.
Related errors
- Command failed with exit code
- Failed to dup stdin
- io::Error::last_os_error()
- Failed to spawn shell
- [ERROR] \n
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/b471b2ad8524186e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/app/shell_command.rs:272
use crossterm::ExecutableCommand;
use std::io::stdout;
// Suspend TUI — best-effort, nothing useful to do on failure.
#[allow(clippy::let_underscore_must_use)]
let _ = disable_raw_mode();
#[allow(clippy::let_underscore_must_use)]
let _ = stdout().execute(LeaveAlternateScreen);
let shell = detect_shell();
let mut child = Command::new(&shell)
.args(["-c", command])
.hide_window()
.spawn()
.map_err(|e| anyhow::anyhow!("Failed to spawn shell: {}", e))?;
let status = child
.wait()
.map_err(|e| anyhow::anyhow!("Failed to wait for command: {}", e))?;
// Resume TUI — best-effort, nothing useful to do on failure.
#[allow(clippy::let_underscore_must_use)]
let _ = stdout().execute(EnterAlternateScreen);
#[allow(clippy::let_underscore_must_use)]
let _ = enable_raw_mode();
// Request a full hard redraw to clear any ghost text from the external command
self.request_full_redraw();
if status.success() {
Ok(())
} else {
anyhow::bail!("Command failed with exit code: {:?}", status.code())
}
}
}
View on GitHub (pinned to 67894ca546)