{"record":{"id":"e2a0f5d53602aea1","repo":"wavetermdev/waveterm","slug":"failed-to-get-stderr-pipe-w-e2a0f5","errorCode":null,"errorMessage":"failed to get stderr pipe: %w","messagePattern":"failed to get stderr pipe: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/remote/connutil.go","lineNumber":137,"sourceCode":"\tvar installCmd bytes.Buffer\n\tif err := installTemplate.Execute(&installCmd, installWords); err != nil {\n\t\treturn fmt.Errorf(\"failed to prepare install command: %w\", err)\n\t}\n\tblocklogger.Infof(ctx, \"[conndebug] copying %q to remote server %q\\n\", wshLocalPath, wavebase.RemoteFullWshBinPath)\n\tgenCmd, err := genconn.MakeSSHCmdClient(client, genconn.CommandSpec{\n\t\tCmd: installCmd.String(),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create remote command: %w\", err)\n\t}\n\tstdin, err := genCmd.StdinPipe()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get stdin pipe: %w\", err)\n\t}\n\tdefer stdin.Close()\n\tstderrBuf, err := genconn.MakeStderrSyncBuffer(genCmd)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get stderr pipe: %w\", err)\n\t}\n\tif err := genCmd.Start(); err != nil {\n\t\treturn fmt.Errorf(\"failed to start remote command: %w\", err)\n\t}\n\tcopyDone := make(chan error, 1)\n\tgo func() {\n\t\tdefer close(copyDone)\n\t\tdefer stdin.Close()\n\t\tif _, err := io.Copy(stdin, input); err != nil && err != io.EOF {\n\t\t\tcopyDone <- fmt.Errorf(\"failed to copy data: %w\", err)\n\t\t} else {\n\t\t\tcopyDone <- nil\n\t\t}\n\t}()\n\tprocErr := genconn.ProcessContextWait(ctx, genCmd)\n\tif procErr != nil {\n\t\treturn fmt.Errorf(\"remote command failed: %w (stderr: %s)\", procErr, stderrBuf.String())\n\t}","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/remote/connutil.go#L119-L155","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nerr := remote.CpWshToRemote(ctx, conn, input, dest)\n// after\nif err := conn.Ping(ctx); err != nil {\n    conn.Close(); conn = remote.DialConnection(ctx, opts) // reconnect first\n}\nerr := remote.CpWshToRemote(ctx, conn, input, dest)","handlingStrategy":"retry","validationCode":"if conn == nil || conn.IsClosed() {\n    return fmt.Errorf(\"remote connection not available\")\n}","typeGuard":"func connReady(c *remote.Conn) bool { return c != nil && !c.IsClosed() }","tryCatchPattern":"err := CpWshToRemote(ctx, conn, input, dest)\nif err != nil && strings.Contains(err.Error(), \"failed to get stderr pipe\") {\n    conn = reconnect(ctx); err = CpWshToRemote(ctx, conn, input, dest)\n}","preventionTips":["Check connection liveness before transfers","Reconnect after any network change","Keep sessions short-lived; don't cache conns across idle periods"],"tags":["go","ssh","remote"],"backgroundTag":"ssh-pipe-setup-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}