wavetermdev/waveterm · error
failed to get stdin pipe: %w
Error message
failed to get stdin pipe: %w
What it means
After creating the remote install command, CpWshToRemote obtains a stdin pipe (used to stream the wsh binary to the remote shell) via genCmd.StdinPipe(). This error is wrapped when acquiring that pipe fails. The binary transfer cannot start without the pipe.
Source
Thrown at pkg/remote/connutil.go:132
installWords := map[string]string{
"installDir": filepath.ToSlash(filepath.Dir(wavebase.RemoteFullWshBinPath)),
"tempPath": wavebase.RemoteFullWshBinPath + ".temp",
"installPath": wavebase.RemoteFullWshBinPath,
}
var installCmd bytes.Buffer
if err := installTemplate.Execute(&installCmd, installWords); err != nil {
return fmt.Errorf("failed to prepare install command: %w", err)
}
blocklogger.Infof(ctx, "[conndebug] copying %q to remote server %q\n", wshLocalPath, wavebase.RemoteFullWshBinPath)
genCmd, err := genconn.MakeSSHCmdClient(client, genconn.CommandSpec{
Cmd: installCmd.String(),
})
if err != nil {
return fmt.Errorf("failed to create remote command: %w", err)
}
stdin, err := genCmd.StdinPipe()
if err != nil {
return fmt.Errorf("failed to get stdin pipe: %w", err)
}
defer stdin.Close()
stderrBuf, err := genconn.MakeStderrSyncBuffer(genCmd)
if err != nil {
return fmt.Errorf("failed to get stderr pipe: %w", err)
}
if err := genCmd.Start(); err != nil {
return fmt.Errorf("failed to start remote command: %w", err)
}
copyDone := make(chan error, 1)
go func() {
defer close(copyDone)
defer stdin.Close()
if _, err := io.Copy(stdin, input); err != nil && err != io.EOF {
copyDone <- fmt.Errorf("failed to copy data: %w", err)
} else {
copyDone <- nil
}View on GitHub (pinned to a4447c1563)
Solutions
- Retry the whole CpWshToRemote operation — transient channel races usually clear on a fresh exec session.
- Verify network stability to the remote host; drops between MakeSSHCmdClient and StdinPipe indicate a flaky connection.
- Check for SSH channel exhaustion (MaxSessions / many concurrent transfers) and serialize installs.
- Inspect the wrapped error for the underlying SSH channel failure and address accordingly.
Defensive patterns
Strategy: retry
Validate before calling
// verify the channel is usable before transfer
if _, _, err := genconn.RunSimpleCommand(ctx, client, genconn.CommandSpec{Cmd: "true"}); err != nil {
return fmt.Errorf("ssh channel unavailable: %w", err)
} Try / catch
err := connutil.CpWshToRemote(ctx, client, os, arch)
if err != nil && strings.Contains(err.Error(), "failed to get stdin pipe") {
// transient channel race — retry once on a fresh session
err = connutil.CpWshToRemote(ctx, reconnect(connName), os, arch)
} Prevention
- Retry the whole copy operation on transient pipe errors; they usually indicate channel races.
- Avoid many concurrent SSH channels to the same host (MaxSessions limits).
- Ensure stable networking; drops between command creation and pipe setup point at flaky links.
- Keep the genconn/SSH client library current to benefit from channel-handling fixes.
When it happens
Trigger: genCmd.StdinPipe() returns an error — typically because the underlying SSH session/channel has already failed or closed after MakeSSHCmdClient succeeded, or the client implementation cannot allocate the pipe.
Common situations: Race where the remote command exits or the channel closes before StdinPipe is acquired; network drop between command creation and pipe setup; resource exhaustion in the SSH library (too many open channels).
Related errors
- failed to create remote command: %w
- nil wshrpc passed to wshclient
- getting ssh connection status: %w
- file info is required
- no route id available
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/2809bb5eaeab7c4e.
Report an issue: GitHub.