wavetermdev/waveterm · error

could not create symlink %s -> %s: %w

Error message

could not create symlink %s -> %s: %w

What it means

ConnServerInitCommand could not create the wsh socket symlink on the remote (or local) host, so the wsh server endpoint is not reachable at the expected path. os.Symlink failed and the target paths are included in the message.

Source

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

func (impl *ServerImpl) ConnServerInitCommand(ctx context.Context, data wshrpc.CommandConnServerInitData) error {
	if data.ClientId == "" {
		return fmt.Errorf("clientid is required")
	}
	if impl.SockName == "" {
		return fmt.Errorf("sockname not set in server impl")
	}
	symlinkPath, err := wavebase.ExpandHomeDir(wavebase.GetPersistentRemoteSockName(data.ClientId))
	if err != nil {
		return fmt.Errorf("cannot expand symlink path: %w", err)
	}
	symlinkDir := filepath.Dir(symlinkPath)

	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 {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Manually remove the stale symlink: rm ~/.waveterm/.../sockname then restart connection
  2. Check the target filesystem supports symlinks (not FAT/some network mounts)
  3. Verify symlinkDir permissions and that MkdirAll succeeded (0700)
  4. Check the wrapped cause (EEXIST/EPERM/ENOSYS) for the exact OS reason
Defensive patterns

Strategy: fallback

Validate before calling

if fi, err := os.Lstat(symlinkPath); err == nil {
    if err := os.Remove(symlinkPath); err != nil {
        return fmt.Errorf("cannot clear stale symlink: %w", err)
    }
}

Try / catch

if err := ConnServerInitCommand(ctx, data); err != nil {
    if strings.Contains(err.Error(), "could not create symlink") {
        // fall back: log and retry after removing stale link
        os.Remove(symlinkPath)
        return ConnServerInitCommand(ctx, data)
    }
    return err
}

Prevention

When it happens

Trigger: os.Symlink(impl.SockName, symlinkPath) fails: path already exists as a file/dir, stale dangling symlink the OS still refuses, unsupported filesystem (Windows/FAT), or missing parent dir permissions despite MkdirAll.

Common situations: Leftover symlink from a crashed previous session on a filesystem that disallows overwrite (os.Remove raced or failed silently), NFS/SSHFS mounts without symlink support, read-only ~/.waveterm dir.

Related errors


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