wavetermdev/waveterm · error
failed to copy data: %w (stderr: %s)
Error message
failed to copy data: %w (stderr: %s)
What it means
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.
Source
Thrown at pkg/remote/connutil.go:158
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())
}
copyErr := <-copyDone
if copyErr != nil {
return fmt.Errorf("failed to copy data: %w (stderr: %s)", copyErr, stderrBuf.String())
}
return nil
}
func IsPowershell(shellPath string) bool {
// get the base path, and then check contains
shellBase := filepath.Base(shellPath)
return strings.Contains(shellBase, "powershell") || strings.Contains(shellBase, "pwsh")
}
func NormalizeConfigPattern(pattern string) string {
userName, err := WaveSshConfigUserSettings().GetStrict(pattern, "User")
if err != nil || userName == "" {
log.Printf("warning: error parsing username of %s for conn dropdown: %v", pattern, err)
localUser, err := user.Current()
if err == nil {
userName = localUser.Username
}View on GitHub (pinned to a4447c1563)
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
Example fix
// before
err := CpWshToRemote(ctx, conn, input, dest) // "failed to copy data: short write (stderr: ...)"
// after
if err := CpWshToRemote(ctx, conn, input, dest); err != nil {
// verify then retry once
if remoteChecksum(ctx, conn, dest) != localChecksum(input) {
err = CpWshToRemote(ctx, conn, input, dest)
}
} Defensive patterns
Strategy: retry
Validate before calling
if fi, err := input.Stat(); err != nil || fi.Size() == 0 {
return fmt.Errorf("input missing or empty")
} Try / catch
err := CpWshToRemote(ctx, conn, input, dest)
if err != nil && strings.Contains(err.Error(), "failed to copy data") {
if checksumMatches(dest, input) { err = nil } else {
input.Seek(0, io.Start); err = CpWshToRemote(ctx, conn, input, dest)
}
} Prevention
- Verify transferred file checksum/size after copy
- Retry transient copy failures once with a fresh connection
- Avoid concurrent writes to the same remote destination
When it happens
Trigger: 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.
Common situations: Remote process exited before consuming the whole stream; partial network failure on the SSH channel; truncated local input file.
Related errors
- reading input: %w
- invalid format of user@host argument
- error running uname -sm: %w, stderr: %s
- unexpected output from uname: %s
- failed to create remote command: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/224e8057e8f7f8d5.
Report an issue: GitHub.