wavetermdev/waveterm · error
failed to start command: %w
Error message
failed to start command: %w
What it means
After building the full command string, Start() calls session.Start(fullCmd) on the golang.org/x/crypto/ssh session. If the remote side refuses to begin execution of the command (channel/request error at the SSH protocol level), the error is wrapped as 'failed to start command'. The session channel exists but the exec request failed.
Source
Thrown at pkg/genconn/ssh-impl.go:78
func (s *SSHProcessController) Start() error {
s.lock.Lock()
defer s.lock.Unlock()
if s.started {
return fmt.Errorf("command already started")
}
fullCmd, err := BuildShellCommand(s.cmdSpec)
if err != nil {
return fmt.Errorf("failed to build shell command: %w", err)
}
// if stdout/stderr weren't piped, then session.stdout/stderr will be nil
// and the library guarantees that the outputs will be attached to io.Discard
// if stdin hasn't been piped, then session.stdin will be nil
// and the libary guarantees that it will be attached to an empty bytes.Buffer, which will produce an immediate EOF
// tl;dr we don't need to worry about hanging beause of long input or explicitly closing stdin
if err := s.session.Start(fullCmd); err != nil {
return fmt.Errorf("failed to start command: %w", err)
}
s.started = true
return nil
}
// Wait waits for the command to complete
func (s *SSHProcessController) Wait() error {
s.once.Do(func() {
s.waitErr = s.session.Wait()
})
return s.waitErr
}
// Kill terminates the command
func (s *SSHProcessController) Kill() {
s.lock.Lock()
defer s.lock.Unlock()
View on GitHub (pinned to a4447c1563)
Solutions
- Check the wrapped error: 'connection lost' style errors mean reconnect and retry; request-failed means inspect remote sshd restrictions.
- Verify the shell/executable named in the CommandSpec exists on the remote host (which bash).
- Test the same command manually over plain ssh to rule out forced-command/Match restrictions.
- Add keepalives to detect dead connections before Start.
Example fix
// before
err := ctrl.Start() // opaque ssh error, controller unusable
// after
if err := ctrl.Start(); err != nil {
if strings.Contains(err.Error(), "connection lost") || strings.Contains(err.Error(), "EOF") {
client = reconnectSSH(cfg)
ctrl, err = genconn.MakeSSHCmdClient(client, spec)
if err == nil { err = ctrl.Start() }
}
if err != nil { return err }
} Defensive patterns
Strategy: retry
Validate before calling
func remoteHasShell(client *ssh.Client, shell string) bool {
out, err := client.NewSession(); if err != nil { return false }
defer out.Close()
err = out.Run("command -v " + shell)
return err == nil
} Try / catch
if err := ctrl.Start(); err != nil {
if isConnError(err) { // EOF, "connection lost", "broken pipe"
client = reconnectSSH(cfg)
ctrl, _ = genconn.MakeSSHCmdClient(client, spec)
return ctrl.Start()
}
return fmt.Errorf("remote refused exec (check sshd restrictions / shell path): %w", err)
} Prevention
- Enable SSH keepalives so dead connections fail fast and predictably.
- Verify the shell/executable named in the spec exists on target hosts (pinned images/AMI baselines).
- Keep sshd Match/ForceCommand restrictions in sync with what your app executes.
- Retry Start on a fresh connection for transient (EOF/reset) wrapped errors.
When it happens
Trigger: Calling Start() when the underlying SSH connection broke between session creation and Start, the remote sshd rejects the exec request, or the requested executable/shell cannot be invoked on the remote side.
Common situations: Network drop right after session creation; remote shell path in CommandSpec does not exist on the host (e.g. zsh not installed); sshd forced-command restrictions rejecting arbitrary exec; server overloaded/out of PTYs.
Related errors
- ssh connection %s not connected, cannot start shellproc
- failed to start process: %w
- failed to create SSH session: %w
- connection %q is not connected
- unable to request connection domain socket: %v
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/f88ed17a464622a0.
Report an issue: GitHub.