herdrdev/herdr · critical
failed to start ssh bridge: {err}
Error message
failed to start ssh bridge: {err} What it means
Spawning the SSH bridge child process (with piped stdin/stdout and inherited stderr) failed; the OS spawn error is wrapped preserving its kind. This happens before any bridging starts, meaning the ssh command itself could not be launched.
Source
Thrown at src/remote/attach.rs:1837
target: &str,
remote_herdr: &RemoteHerdr,
session_name: &str,
ssh_options: Option<&ManagedSshOptions>,
_bridge_stop: &Arc<AtomicBool>,
) -> io::Result<()> {
let mut command = Command::new("ssh");
apply_managed_ssh_options(&mut command, ssh_options);
command
.arg("-T")
.arg(target)
.arg(remote_bridge_command(remote_herdr, session_name))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit());
let mut child = command
.spawn()
.map_err(|err| io::Error::new(err.kind(), format!("failed to start ssh bridge: {err}")))?;
let mut child_stdin = child
.stdin
.take()
.ok_or_else(|| io::Error::new(io::ErrorKind::BrokenPipe, "ssh bridge stdin missing"))?;
let mut child_stdout = child
.stdout
.take()
.ok_or_else(|| io::Error::new(io::ErrorKind::BrokenPipe, "ssh bridge stdout missing"))?;
let mut stream_to_child = stream.try_clone()?;
let mut child_to_stream = stream;
let upload = thread::spawn(move || {
let _ = copy_flush(&mut stream_to_child, &mut child_stdin);
});
let download = thread::spawn(move || {
let _ = copy_flush(&mut child_stdout, &mut child_to_stream);
let _ = crate::ipc::shutdown_local_stream_write(&child_to_stream);
});View on GitHub (pinned to f457cff4f2)
Solutions
- Verify ssh is installed: ssh -V
- Install openssh-client on the local machine
- Fix PATH for non-interactive environments (set it in the shell profile herdr inherits)
- Retry when the machine is not under resource pressure
Example fix
# before: no ssh client # after apt-get update && apt-get install -y openssh-client
Defensive patterns
Strategy: validation
Validate before calling
fn ssh_available() -> bool {
std::process::Command::new("ssh")
.arg("-V")
.output()
.is_ok()
} Try / catch
match attach() {
Err(e) if e.to_string().contains("failed to start ssh bridge") => {
eprintln!("check that ssh is installed and on PATH");
Err(e)
}
r => r,
} Prevention
- Verify ssh -V works in the environment hosting herdr
- Install openssh-client in container images that run attach
When it happens
Trigger: The ssh executable is missing from PATH, not executable, argument list issues, or process/resource limits preventing spawn when establishing the local-to-remote stdio bridge.
Common situations: Containers or minimal hosts without openssh-client, PATH stripped in non-interactive shells, fork failures under memory pressure.
Related errors
- ssh bridge stdin missing
- ssh bridge stdout missing
- ssh bridge exited with {status}
- remote bridge upload failed: {err}
- remote bridge download failed: {err}
AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28).
Data as JSON: /api/errors/870cab491e280ae0.
Report an issue: GitHub.