{"record":{"id":"366e42a1a623073c","repo":"wavetermdev/waveterm","slug":"remote-command-failed-w-stderr-s","errorCode":null,"errorMessage":"remote command failed: %w (stderr: %s)","messagePattern":"remote command failed: %w \\(stderr: (.+?)\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/remote/connutil.go","lineNumber":154,"sourceCode":"\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}\n\tcopyErr := <-copyDone\n\tif copyErr != nil {\n\t\treturn fmt.Errorf(\"failed to copy data: %w (stderr: %s)\", copyErr, stderrBuf.String())\n\t}\n\treturn nil\n}\n\nfunc IsPowershell(shellPath string) bool {\n\t// get the base path, and then check contains\n\tshellBase := filepath.Base(shellPath)\n\treturn strings.Contains(shellBase, \"powershell\") || strings.Contains(shellBase, \"pwsh\")\n}\n\nfunc NormalizeConfigPattern(pattern string) string {\n\tuserName, err := WaveSshConfigUserSettings().GetStrict(pattern, \"User\")\n\tif err != nil || userName == \"\" {\n\t\tlog.Printf(\"warning: error parsing username of %s for conn dropdown: %v\", pattern, err)","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/remote/connutil.go#L136-L172","documentation":"CpWshToRemote waits for the remote command process to finish via genconn.ProcessContextWait. If the remote process exits nonzero or is killed (including by context cancellation), the error is wrapped along with everything the command wrote to stderr.","triggerScenarios":"UpdateWsh/InstallWsh copying wsh to a remote where the receiving command fails: wrong permissions on destination, context deadline exceeded, remote shell reporting an error, disk full.","commonSituations":"Destination directory not writable by the SSH user; remote filesystem full while writing the new wsh binary; user cancelled the operation; remote wsh refusing overwrite of a running binary (ETXTBSY-like behavior).","solutions":["Read the stderr text embedded in the error — it states the remote-side cause","Fix remote destination permissions or choose a writable install path","Stop the running remote wsh instance before overwriting the binary","Retry; if caused by ctx cancellation, re-run with a longer/valid context"],"exampleFix":"// before\nerr := CpWshToRemote(ctx, conn, input, \"/usr/local/bin/wsh\") // permission denied in stderr\n// after\ndest := os.ExpandEnv(\"$HOME/.local/bin/wsh\") // user-writable path\nerr := CpWshToRemote(ctx, conn, input, dest)","handlingStrategy":"try-catch","validationCode":"if ok, _ := remotePathWritable(ctx, conn, filepath.Dir(dest)); !ok {\n    return fmt.Errorf(\"destination dir %q not writable on remote\", filepath.Dir(dest))\n}","typeGuard":null,"tryCatchPattern":"var remoteCmdErr *RemoteCmdError\nerr := CpWshToRemote(ctx, conn, input, dest)\nif errors.As(err, &remoteCmdErr) {\n    log.Errorf(\"remote stderr: %s\", remoteCmdErr.Stderr) // diagnose from stderr\n}","preventionTips":["Stop the running wsh binary before overwriting it","Use a user-writable install path","Set adequate context deadlines and surface ctx cancellation to users"],"tags":["go","ssh","process"],"backgroundTag":"remote-command-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}