diff --git a/pkg/client/connector/sshcli.go b/pkg/client/connector/sshcli.go index bd0b1d6a..14d9fb8f 100644 --- a/pkg/client/connector/sshcli.go +++ b/pkg/client/connector/sshcli.go @@ -45,9 +45,13 @@ func controlSocketPath() string { // of the ProxyJump option. This ensures that shared connections are uniquely identified. sockName := fmt.Sprintf("uc_control_%%C.sock") - // Prefer XDG_RUNTIME_DIR if set, fall back to ~/.ssh if it exists. + // Prefer XDG_RUNTIME_DIR if set and the directory exists, fall back to ~/.ssh if it exists. if dir := os.Getenv("XDG_RUNTIME_DIR"); dir != "" { - return filepath.Join(dir, sockName) + // On WSL2 without systemd, XDG_RUNTIME_DIR may be set to /run/user/$UID that doesn't actually exist, + // so existence must be verified before use: https://github.com/psviderski/uncloud/issues/319. + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + return filepath.Join(dir, sockName) + } } if home, err := os.UserHomeDir(); err == nil { sshDir := filepath.Join(home, ".ssh") diff --git a/pkg/client/connector/sshcli_test.go b/pkg/client/connector/sshcli_test.go index 0445dff0..e18eec83 100644 --- a/pkg/client/connector/sshcli_test.go +++ b/pkg/client/connector/sshcli_test.go @@ -109,11 +109,20 @@ func TestControlSocketPath(t *testing.T) { assert.True(t, strings.HasSuffix(path1, ".sock")) assert.Contains(t, path1, "%C") - t.Run("uses XDG_RUNTIME_DIR when set", func(t *testing.T) { - runDir := "/user/runtime/dir" + t.Run("uses XDG_RUNTIME_DIR when set and exists", func(t *testing.T) { + runDir := t.TempDir() t.Setenv("XDG_RUNTIME_DIR", runDir) path := controlSocketPath() assert.True(t, strings.HasPrefix(path, runDir)) }) + + t.Run("falls back when XDG_RUNTIME_DIR is set but missing", func(t *testing.T) { + // WSL2 without systemd sets XDG_RUNTIME_DIR to a path that doesn't exist. + t.Setenv("XDG_RUNTIME_DIR", "/nonexistent/uncloud-test-xdg") + + path := controlSocketPath() + assert.NotEmpty(t, path) + assert.False(t, strings.HasPrefix(path, "/nonexistent/")) + }) }