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
- Re-establish the remote connection (disconnect and reconnect) and retry UpdateWsh/InstallWsh
- Verify the SSH connection is alive before calling (e.g. run a trivial remote command first)
- 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
- Check connection liveness before transfers
- Reconnect after any network change
- Keep sessions short-lived; don't cache conns across idle periods
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
- starting file stream: %w
- failed to reconnect to job: %w
- invalid ssh remote name (%s): %w
- connection %q not found
- failed to start durable shell: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/e2a0f5d53602aea1.
Report an issue: GitHub.