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

  1. Retry the whole CpWshToRemote operation — transient channel races usually clear on a fresh exec session.
  2. Verify network stability to the remote host; drops between MakeSSHCmdClient and StdinPipe indicate a flaky connection.
  3. Check for SSH channel exhaustion (MaxSessions / many concurrent transfers) and serialize installs.
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/2809bb5eaeab7c4e. Report an issue: GitHub.