{"record":{"id":"224e8057e8f7f8d5","repo":"wavetermdev/waveterm","slug":"failed-to-copy-data-w-stderr-s","errorCode":null,"errorMessage":"failed to copy data: %w (stderr: %s)","messagePattern":"failed to copy data: %w \\(stderr: (.+?)\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/remote/connutil.go","lineNumber":158,"sourceCode":"\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)\n\t\tlocalUser, err := user.Current()\n\t\tif err == nil {\n\t\t\tuserName = localUser.Username\n\t\t}","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/remote/connutil.go#L140-L176","documentation":"After the remote command in CpWshToRemote exits successfully, the code reads the copyDone channel. If the stdin io.Copy goroutine reported a copy error, it is re-wrapped with the remote stderr contents for diagnosis.","triggerScenarios":"The stdin copy goroutine failed (e.g. writing to a closed pipe after the remote process exited early) during UpdateWsh/InstallWsh, and the remote process still reported success — the error then includes stderr for context.","commonSituations":"Remote process exited before consuming the whole stream; partial network failure on the SSH channel; truncated local input file.","solutions":["Check the stderr text in the error for the remote-side explanation","Retry the copy — transient SSH channel breaks are the usual cause","Verify the input file is complete and unchanged during transfer","Compare wsh binary size/checksum on remote after retry to confirm integrity"],"exampleFix":"// before\nerr := CpWshToRemote(ctx, conn, input, dest) // \"failed to copy data: short write (stderr: ...)\"\n// after\nif err := CpWshToRemote(ctx, conn, input, dest); err != nil {\n    // verify then retry once\n    if remoteChecksum(ctx, conn, dest) != localChecksum(input) {\n        err = CpWshToRemote(ctx, conn, input, dest)\n    }\n}","handlingStrategy":"retry","validationCode":"if fi, err := input.Stat(); err != nil || fi.Size() == 0 {\n    return fmt.Errorf(\"input missing or empty\")\n}","typeGuard":null,"tryCatchPattern":"err := CpWshToRemote(ctx, conn, input, dest)\nif err != nil && strings.Contains(err.Error(), \"failed to copy data\") {\n    if checksumMatches(dest, input) { err = nil } else {\n        input.Seek(0, io.Start); err = CpWshToRemote(ctx, conn, input, dest)\n    }\n}","preventionTips":["Verify transferred file checksum/size after copy","Retry transient copy failures once with a fresh connection","Avoid concurrent writes to the same remote destination"],"tags":["go","io","ssh"],"backgroundTag":"stream-copy-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}