wavetermdev/waveterm · error

cannot expand wsh path

Error message

cannot expand wsh path: %w

What it means

getWshPath failed expanding '~/.waveterm/bin/wsh' via wavebase.ExpandHomeDir for a remote server, meaning the user's home directory could not be resolved on the remote host. Used by RemoteStartJobCommand to locate the remote wsh binary.

Solutions

  1. Ensure HOME is set for the SSH user on the remote host
  2. Use a normal shell account rather than a chrooted/restricted one
  3. Verify wavebase.ExpandHomeDir behavior for the target platform
  4. Note: only non-local servers hit this path; local builds return the data dir directly
Defensive patterns

Strategy: validation

Validate before calling

out, err := runOnRemote("echo $HOME")
if err != nil || strings.TrimSpace(out) == "" {
    return fmt.Errorf("remote HOME not set; cannot resolve wsh path")
}

Try / catch

path, err := impl.getWshPath()
if err != nil {
    return fmt.Errorf("remote wsh unavailable (check HOME on remote): %w", err)
}

Prevention

When it happens

Trigger: Calling RemoteStartJobCommand on a remote connection where ExpandHomeDir('~/.waveterm/bin/wsh') errors — HOME unset or unparseable for the SSH user.

Common situations: SSH into accounts with no/odd HOME env (restricted shells, nologin users), sftp-only chrooted accounts, Windows remote without proper HOME mapping.

Related errors


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

Appendix: source

Thrown at pkg/wshrpc/wshremote/wshremote.go:136

	if err := os.MkdirAll(symlinkDir, 0700); err != nil {
		return fmt.Errorf("could not create client directory %s: %w", symlinkDir, err)
	}
	os.Remove(symlinkPath)
	if err := os.Symlink(impl.SockName, symlinkPath); err != nil {
		return fmt.Errorf("could not create symlink %s -> %s: %w", symlinkPath, impl.SockName, err)
	}
	impl.Log("created symlink %s -> %s\n", symlinkPath, impl.SockName)
	return nil
}

func (impl *ServerImpl) getWshPath() (string, error) {
	if impl.IsLocal {
		return filepath.Join(wavebase.GetWaveDataDir(), "bin", "wsh"), nil
	}
	wshPath, err := wavebase.ExpandHomeDir("~/.waveterm/bin/wsh")
	if err != nil {
		return "", fmt.Errorf("cannot expand wsh path: %w", err)
	}
	return wshPath, nil
}

func (impl *ServerImpl) BadgeWatchPidCommand(ctx context.Context, data wshrpc.CommandBadgeWatchPidData) error {
	if data.Pid <= 0 {
		return fmt.Errorf("invalid pid: %d", data.Pid)
	}
	if data.ORef.IsEmpty() {
		return fmt.Errorf("oref is required")
	}
	if data.BadgeId == "" {
		return fmt.Errorf("badgeid is required")
	}
	go func() {
		defer func() {
			panichandler.PanicHandler("BadgeWatchPidCommand", recover())
		}()

View on GitHub (pinned to a4447c1563)