wavetermdev/waveterm · error

error copying wsh binary to bin: %v

Error message

error copying wsh binary to bin: %v

What it means

initCustomShellStartupFilesInternal copies the platform-appropriate wsh binary into <waveDataHome>/bin via utilfn.AtomicRenameCopy (write temp + rename). This error wraps any failure in that copy: source missing/unreadable, destination not writable, cross-device rename issues, or target binary in use (ETXTBSY/Windows lock).

Source

Thrown at pkg/util/shellutil/shellutil.go:458

		return err
	}

	// copy the correct binary to bin
	wshFullPath, err := GetLocalWshBinaryPath(wavebase.WaveVersion, runtime.GOOS, runtime.GOARCH)
	if err != nil {
		log.Printf("error (non-fatal), could not resolve wsh binary path: %v\n", err)
	}
	if _, err := os.Stat(wshFullPath); err != nil {
		log.Printf("error (non-fatal), could not resolve wsh binary %q: %v\n", wshFullPath, err)
		return nil
	}
	wshDstPath := filepath.Join(binDir, "wsh")
	if runtime.GOOS == "windows" {
		wshDstPath = wshDstPath + ".exe"
	}
	err = utilfn.AtomicRenameCopy(wshDstPath, wshFullPath, 0755)
	if err != nil {
		return fmt.Errorf("error copying wsh binary to bin: %v", err)
	}
	wshBaseName := filepath.Base(wshFullPath)
	log.Printf("wsh binary successfully copied from %q to %q\n", wshBaseName, wshDstPath)
	return nil
}

func GetShellTypeFromShellPath(shellPath string) string {
	shellBase := filepath.Base(shellPath)
	if strings.Contains(shellBase, "bash") {
		return ShellType_bash
	}
	if strings.Contains(shellBase, "zsh") {
		return ShellType_zsh
	}
	if strings.Contains(shellBase, "fish") {
		return ShellType_fish
	}
	if strings.Contains(shellBase, "pwsh") || strings.Contains(shellBase, "powershell") {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify the source binary exists via GetLocalWshBinaryPath(wavebase.WaveVersion, GOOS, GOARCH) and reinstall/repair Wave if missing.
  2. Check write permission and free space on the wave bin dir (<data>/bin); fix ownership.
  3. Stop other running Wave instances/wsh processes that may hold the destination binary open, then retry.
  4. If the source-stat guard silently skipped (non-fatal log path), rerun after the wsh binary is properly installed.

Example fix

// before: stale running wsh holds the destination lock
$ pkill wsh && wave  # then init succeeds
// after
wsh binary successfully copied from "wsh" to ".../bin/wsh"
Defensive patterns

Strategy: retry

Validate before calling

// pre-check source and destination before the copy
src, _ := shellutil.GetLocalWshBinaryPath(version, runtime.GOOS, runtime.GOARCH)
if _, err := os.Stat(src); err != nil { return fmt.Errorf("wsh source missing: %w", err) }
if err := os.MkdirAll(binDir, 0755); err != nil { return err }

Try / catch

err := shellutil.InitCustomShellStartupFiles() // or the internal init
if err != nil && strings.Contains(err.Error(), "error copying wsh binary") {
	time.Sleep(500 * time.Millisecond) // let AV/running-process lock release
	if retryErr := shellutil.InitCustomShellStartupFiles(); retryErr != nil {
		return retryErr
	}
}

Prevention

When it happens

Trigger: Calling initCustomShellStartupFilesInternal (wsh init path) when AtomicRenameCopy(wshDstPath, wshFullPath, 0755) fails: wshFullPath missing despite passing os.Stat, bin dir unwritable, or rename across filesystems.

Common situations: Upgraded Wave package where the bundled wsh path changed; running Wave from a read-only install while the data dir is elsewhere; two Wave instances racing to replace wsh simultaneously; EPERM when overwriting a running executable on Windows.

Related errors


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