wavetermdev/waveterm · error

failed to create remote command: %w

Error message

failed to create remote command: %w

What it means

CpWshToRemote creates an SSH exec session via genconn.MakeSSHCmdClient to run the rendered install command on the remote host. This error is wrapped when creating that remote command client fails. It indicates the SSH layer could not set up the exec request, so nothing was streamed to the remote host.

Source

Thrown at pkg/remote/connutil.go:128

	if err != nil {
		return fmt.Errorf("cannot open local file %s: %w", wshLocalPath, err)
	}
	defer input.Close()
	installWords := map[string]string{
		"installDir":  filepath.ToSlash(filepath.Dir(wavebase.RemoteFullWshBinPath)),
		"tempPath":    wavebase.RemoteFullWshBinPath + ".temp",
		"installPath": wavebase.RemoteFullWshBinPath,
	}
	var installCmd bytes.Buffer
	if err := installTemplate.Execute(&installCmd, installWords); err != nil {
		return fmt.Errorf("failed to prepare install command: %w", err)
	}
	blocklogger.Infof(ctx, "[conndebug] copying %q to remote server %q\n", wshLocalPath, wavebase.RemoteFullWshBinPath)
	genCmd, err := genconn.MakeSSHCmdClient(client, genconn.CommandSpec{
		Cmd: installCmd.String(),
	})
	if err != nil {
		return fmt.Errorf("failed to create remote command: %w", err)
	}
	stdin, err := genCmd.StdinPipe()
	if err != nil {
		return fmt.Errorf("failed to get stdin pipe: %w", err)
	}
	defer stdin.Close()
	stderrBuf, err := genconn.MakeStderrSyncBuffer(genCmd)
	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 {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify the SSH connection is still alive (reconnect if idle) and retry InstallWsh/UpdateWsh.
  2. Check remote sshd config for MaxSessions limits or ForceCommand restrictions blocking exec channels.
  3. Ensure the genconn client passed to CpWshToRemote is the live client for the target connection, not a stale one.
  4. Look at the wrapped error (%w) for the underlying SSH failure reason (e.g. 'connection lost', 'channel open failure').

Example fix

// before
client := staleClient // connection already closed
err := connutil.CpWshToRemote(ctx, client, os, arch)
// after
if client == nil || isClosed(client) {
    client = reconnect(connName)
}
err := connutil.CpWshToRemote(ctx, client, os, arch)
Defensive patterns

Strategy: retry

Validate before calling

// ensure the SSH client is alive before install
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
if _, _, err := genconn.RunSimpleCommand(timeoutCtx, client, genconn.CommandSpec{Cmd: "true"}); err != nil {
    return fmt.Errorf("ssh connection not ready: %w", err)
}

Try / catch

err := connutil.CpWshToRemote(ctx, client, os, arch)
if err != nil && strings.Contains(err.Error(), "failed to create remote command") {
    client = reconnect(connName)
    err = connutil.CpWshToRemote(ctx, client, os, arch)
}

Prevention

When it happens

Trigger: genconn.MakeSSHCmdClient returns an error: underlying SSH client is disconnected/closed, SSH session allocation fails (server refuses exec), connection dropped between connect and command creation, or the command spec is rejected by the client implementation.

Common situations: SSH session limits (MaxSessions) exhausted on the server; connection idle-timed out just before wsh install/update; server-side restriction (ForceCommand, restricted shell) preventing exec; using a client object after the connection was closed.

Related errors


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