@@ -382,13 +382,23 @@ func ensureServer(cmd *cobra.Command, hostURL *url.URL) error {
382 switch hostURL.Scheme {
382 switch hostURL.Scheme {
383 case "unix", "npipe":
383 case "unix", "npipe":
384 needsStart := false
384 needsStart := false
385+ _, statErr := os.Stat(hostURL.Host)
387+ case statErr == nil:
388+ restarted, err := restartIfStale(cmd, hostURL)
390+ slog.Warn("Failed to check server version", "error", err)
392+ needsStart = restarted || err != nil
385− if _, err := os.Stat(hostURL.Host); err != nil && errors.Is(err, fs.ErrNotExist) {
393+ case errors.Is(statErr, fs.ErrNotExist):
386 needsStart = true
394 needsStart = true
387− } else if err == nil {
395+ default:
396+ slog.Warn("Unexpected error stat'ing server socket, attempting cleanup",
397+ "path", hostURL.Host, "error", statErr)
388− if err := restartIfStale(cmd, hostURL); err != nil {
398+ if err := os.Remove(hostURL.Host); err != nil && !errors.Is(err, fs.ErrNotExist) {
389− slog.Warn("Failed to check server version, restarting", "error", err)
399+ return fmt.Errorf("failed to remove stale server socket %q: %v", hostURL.Host, err)
394 if needsStart {
404 if needsStart {
@@ -517,17 +527,21 @@ func probeHealth(ctx context.Context, h *http.Client, reqURL string, hostURL *ur
517// restartIfStale checks whether the running server matches the current
527// restartIfStale checks whether the running server matches the current
518// client version. When they differ, it sends a shutdown command and
528// client version. When they differ, it sends a shutdown command and
519// removes the stale socket so the caller can start a fresh server.
529// removes the stale socket so the caller can start a fresh server.
531+// It returns restarted=true when it has shut down a stale server and the
532+// caller must spawn a new one. When the server matches the client version
533+// (or the check itself fails), restarted is false.
520−func restartIfStale(cmd *cobra.Command, hostURL *url.URL) error {
534+func restartIfStale(cmd *cobra.Command, hostURL *url.URL) (restarted bool, err error) {
521 c, err := client.NewClient("", hostURL.Scheme, hostURL.Host)
535 c, err := client.NewClient("", hostURL.Scheme, hostURL.Host)
522 if err != nil {
536 if err != nil {
523− return err
537+ return false, err
525 vi, err := c.VersionInfo(cmd.Context())
539 vi, err := c.VersionInfo(cmd.Context())
526 if err != nil {
540 if err != nil {
527− return err
541+ return false, err
529 if vi.Version == version.Version {
543 if vi.Version == version.Version {
530− return nil
544+ return false, nil
532 slog.Info("Server version mismatch, restarting",
546 slog.Info("Server version mismatch, restarting",
533 "server", vi.Version,
547 "server", vi.Version,
@@ -541,13 +555,13 @@ func restartIfStale(cmd *cobra.Command, hostURL *url.URL) error {
543 case <-cmd.Context().Done():
557 case <-cmd.Context().Done():
544− return cmd.Context().Err()
558+ return true, cmd.Context().Err()
545 case <-time.After(100 * time.Millisecond):
559 case <-time.After(100 * time.Millisecond):
548 // Force-remove if the socket is still lingering.
562 // Force-remove if the socket is still lingering.
549 _ = os.Remove(hostURL.Host)
563 _ = os.Remove(hostURL.Host)
550− return nil
564+ return true, nil
553var safeNameRegexp = regexp.MustCompile(`[^a-zA-Z0-9._-]`)
567var safeNameRegexp = regexp.MustCompile(`[^a-zA-Z0-9._-]`)