wavetermdev/waveterm · error

remote command failed: %w (stderr: %s)

Error message

remote command failed: %w (stderr: %s)

What it means

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.

Source

Thrown at pkg/remote/connutil.go:154

	if err != nil {
		return fmt.Errorf("failed to get stderr pipe: %w", err)
	}
	if err := genCmd.Start(); err != nil {
		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)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Read the stderr text embedded in the error — it states the remote-side cause
  2. Fix remote destination permissions or choose a writable install path
  3. Stop the running remote wsh instance before overwriting the binary
  4. Retry; if caused by ctx cancellation, re-run with a longer/valid context

Example fix

// before
err := CpWshToRemote(ctx, conn, input, "/usr/local/bin/wsh") // permission denied in stderr
// after
dest := os.ExpandEnv("$HOME/.local/bin/wsh") // user-writable path
err := CpWshToRemote(ctx, conn, input, dest)
Defensive patterns

Strategy: try-catch

Validate before calling

if ok, _ := remotePathWritable(ctx, conn, filepath.Dir(dest)); !ok {
    return fmt.Errorf("destination dir %q not writable on remote", filepath.Dir(dest))
}

Try / catch

var remoteCmdErr *RemoteCmdError
err := CpWshToRemote(ctx, conn, input, dest)
if errors.As(err, &remoteCmdErr) {
    log.Errorf("remote stderr: %s", remoteCmdErr.Stderr) // diagnose from stderr
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


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