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

  1. Check the stderr text in the error for the remote-side explanation
  2. Retry the copy — transient SSH channel breaks are the usual cause
  3. Verify the input file is complete and unchanged during transfer
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/224e8057e8f7f8d5. Report an issue: GitHub.