wavetermdev/waveterm · error

failed to get stderr pipe: %w

Error message

failed to get stderr pipe: %w

What it means

CpWshToRemote streams a file to a remote host over an SSH connection. Before starting the remote command it opens the command's stdin and builds a synchronized stderr capture buffer via genconn.MakeStderrSyncBuffer; this error is wrapped when that stderr pipe cannot be created.

Source

Thrown at pkg/remote/connutil.go:137

	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
		}
	}()
	procErr := genconn.ProcessContextWait(ctx, genCmd)
	if procErr != nil {
		return fmt.Errorf("remote command failed: %w (stderr: %s)", procErr, stderrBuf.String())
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Re-establish the remote connection (disconnect and reconnect) and retry UpdateWsh/InstallWsh
  2. Verify the SSH connection is alive before calling (e.g. run a trivial remote command first)
  3. Update wsh on both sides to matching versions and retry

Example fix

// before
err := remote.CpWshToRemote(ctx, conn, input, dest)
// after
if err := conn.Ping(ctx); err != nil {
    conn.Close(); conn = remote.DialConnection(ctx, opts) // reconnect first
}
err := remote.CpWshToRemote(ctx, conn, input, dest)
Defensive patterns

Strategy: retry

Validate before calling

if conn == nil || conn.IsClosed() {
    return fmt.Errorf("remote connection not available")
}

Type guard

func connReady(c *remote.Conn) bool { return c != nil && !c.IsClosed() }

Try / catch

err := CpWshToRemote(ctx, conn, input, dest)
if err != nil && strings.Contains(err.Error(), "failed to get stderr pipe") {
    conn = reconnect(ctx); err = CpWshToRemote(ctx, conn, input, dest)
}

Prevention

When it happens

Trigger: Calling UpdateWsh or InstallWsh (which call CpWshToRemote) when the underlying connection cannot provide a stderr pipe for the generated command — typically a failed/broken SSH session connection object.

Common situations: The remote connection dropped before the copy started; the connection's session factory returns a command whose StderrPipe setup fails (e.g. session already started or closed); stale connection cached after network change.

Related errors


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