Subject: Re: bug#70724: 29.2.50; eglot-reconnect errors when the project is deleted

From: Spencer Baugh <[email-masked]>
Date: Thu, 21 Nov 2024 15:51:36 -0500

João Távora <[email-masked]> writes:

> Explain your scenario, please.  And note that if you take the rug from
> underneath Eglot it will error, period.  That's by design.   I'm not
> willing to contort the code to do  anything more than provide a
> more or less understandable error message for such (relatively rare)
> rug pulling.  IOW sentinels erroring is part of Emacs life and not a
> bug in itself,  it's the error message that I'm willing to help ensure is
> clear.

I use (the equivalent of) git worktrees to have one repo checkout per
branch that I'm working on.  As I finish work on a branch, I delete the
worktree with a shell command outside of Emacs.  Thus I hit this error
several times a day, depending on how productive I am.

(The same workflow is used by ~everyone at my employer, and before we
can use Eglot widely, it would be ideal to get rid of this frequent
error)

> Please provide a backtrace and say if it's obtained with the very
> same recipe in the beginning of the bug report.
>

Sure, here's the backtrace with the patch in your last email.  It is
obtained with the very same recipe in the beginning of the bug report: I
opened an (OCaml) file, did M-x eglot (starting ocaml-lsp), deleted the
directory, and it proceeded as I said in the beginning of the bug
report.

Debugger entered--Lisp error: (error "[eglot] Project at `/usr/local/home/sbaugh/workspaces/fe-880764/+share+/' is gone!")
  signal(error ("[eglot] Project at `/usr/local/home/sbaugh/workspaces/fe-880764/+share+/' is gone!"))
  error("[eglot] %s" "Project at `/usr/local/home/sbaugh/workspaces/fe-880764/+share+/' is gone!")
  eglot--error("Project at `%s' is gone!" "/usr/local/home/sbaugh/workspaces/fe-880764/+share+/")
  (if (file-exists-p default-directory) (project-name project) (eglot--error "Project at `%s' is gone!" default-directory))
  (let* ((default-directory (project-root project)) (nickname (if (file-exists-p default-directory) (project-name project) (eglot--error "Project at `%s' is gone!" default-directory))) (readable-name (format "EGLOT (%s/%s)" nickname managed-modes)) server-info (contact (if (functionp contact) (funcall contact) contact)) (initargs (cond ((keywordp (car contact)) contact) ((integerp (car (cdr contact))) (setq server-info (list (format "%s:%s" ... ...))) (list ':process #'(lambda nil ...))) ((and (stringp (car contact)) (cl-find-if #'... contact)) (setq server-info (list "<inferior process>")) (list ':process (jsonrpc-autoport-bootstrap readable-name contact :connect-args '...))) ((stringp (car contact)) (let* ((probe ...) (more-initargs ...) (contact ...)) (cons ':process (cons ... more-initargs)))))) (spread #'(lambda (fn) #'(lambda (server method params) (let ... ...)))) (server (apply #'make-instance class :name readable-name :events-buffer-config eglot-events-buffer-config :notification-dispatcher (funcall spread #'eglot-handle-notification) :request-dispatcher (funcall spread #'eglot-handle-request) :on-shutdown #'eglot--on-shutdown initargs)) (canceled nil) (tag (make-symbol "connected-catch-tag"))) (if server-info (progn (jsonrpc--debug server "Running language server: %s" (string-join server-info " ")))) (let* ((v server)) (\(setf\ eglot--saved-initargs\) initargs v)) (let* ((v server)) (\(setf\ eglot--project\) project v)) (let* ((v server)) (\(setf\ eglot--project-nickname\) nickname v)) (let* ((v server)) (\(setf\ eglot--languages\) (let* ((--cl-var-- managed-modes) (m nil) (--cl-var-- language-ids) (l nil) (--cl-var-- nil)) (while (and (consp --cl-var--) (progn (setq m ...) (consp --cl-var--))) (setq l (car --cl-var--)) (setq --cl-var-- (cons (cons m l) --cl-var--)) (setq --cl-var-- (cdr --cl-var--)) (setq --cl-var-- (cdr --cl-var--))) (nreverse --cl-var--)) v)) (run-hook-with-args 'eglot-server-initialized-hook server) (unwind-protect (condition-case _quit (let ((retval (catch tag (jsonrpc-async-request server :initialize ... :success-fn ... :timeout eglot-connect-timeout :error-fn ... :timeout-fn ...) (cond ... ...)))) (cond ((consp retval) (let* (...) (if ... ... ...))) ((null retval) (let nil (eglot--message "Waiting in background for server `%s'" ...) nil)) (t (let nil server)))) (quit (jsonrpc-shutdown server) (setq canceled 'quit))) (setq tag nil)))
  eglot--connect((tuareg-mode) #s(jane-fe-project :workspace "/usr/local/home/sbaugh/workspaces/fe-880764/+share+/" :vc (vc Hg "/usr/local/home/sbaugh/workspaces/fe-880764/+share+/")) eglot-lsp-server (:process (closure ((contact "dispatch_ocaml_lsp.exe" "open! Core" "dev") (server-info "dispatch_ocaml_lsp.exe" "open! Core" "dev") (readable-name . "EGLOT (dune-add-disable-file-watcher-rpc/(tuareg-mode))") tramp-ssh-controlmaster-options tramp-use-ssh-controlmaster-options) nil (let ((default-directory default-directory) (tramp-use-ssh-controlmaster-options 'suppress) (tramp-ssh-controlmaster-options "-o ControlMaster=no -o ControlPath=none")) (make-process :name readable-name :command (setq server-info (eglot--cmd contact)) :connection-type 'pipe :coding 'utf-8-emacs-unix :noquery t :stderr (get-buffer-create (format "*%s stderr*" readable-name)) :file-handler t)))) ("tuareg"))
  eglot-reconnect(#<eglot-lsp-server eglot-lsp-server-18f8056>)
  (cond ((eglot--shutdown-requested server) t) ((not (eglot--inhibit-autoreconnect server)) (eglot--warn "Reconnecting after unexpected server exit.") (eglot-reconnect server)) ((timerp (eglot--inhibit-autoreconnect server)) (eglot--warn "Not auto-reconnecting, last one didn't last long.")))
  eglot--on-shutdown(#<eglot-lsp-server eglot-lsp-server-18f8056>)
  funcall(eglot--on-shutdown #<eglot-lsp-server eglot-lsp-server-18f8056>)
  (unwind-protect (mapc #'(lambda (jsonrpc-lambda-elem168) (apply #'(lambda (_id _method _success-fn error-fn _timer) (funcall error-fn '(:code -1 :message "Server died"))) jsonrpc-lambda-elem168)) (jsonrpc--continuations connection)) (jsonrpc--message "Server exited with status %s" (process-exit-status proc)) (delete-process proc) (let* ((p (and t (slot-value connection '-autoport-inferior)))) (if p (delete-process p) nil)) (funcall (jsonrpc--on-shutdown connection) connection))
  (progn (save-current-buffer (set-buffer (jsonrpc-events-buffer connection)) (let ((inhibit-read-only t)) (insert "\n----------b---y---e---b---y---e----------\n"))) (mapc #'(lambda (jsonrpc-lambda-elem167) (apply #'(lambda (_id _method _success-fn _error-fn timer) (if timer (progn (cancel-timer timer)))) jsonrpc-lambda-elem167)) (jsonrpc--continuations connection)) (maphash #'(lambda (_ triplet) (progn (ignore (consp triplet)) (let* ((x919 (cdr-safe triplet))) (progn (ignore (consp x919)) (let* ((x920 (car-safe x919)) (x921 (cdr-safe x919))) (progn (ignore (consp x921)) (let* ((x923 (cdr-safe x921))) (progn (ignore (null x923)) (let ((timer x920)) (if timer (progn (cancel-timer timer)))))))))))) (jsonrpc--deferred-actions connection)) (process-put proc 'jsonrpc-sentinel-cleanup-started t) (unwind-protect (mapc #'(lambda (jsonrpc-lambda-elem168) (apply #'(lambda (_id _method _success-fn error-fn _timer) (funcall error-fn '(:code -1 :message "Server died"))) jsonrpc-lambda-elem168)) (jsonrpc--continuations connection)) (jsonrpc--message "Server exited with status %s" (process-exit-status proc)) (delete-process proc) (let* ((p (and t (slot-value connection '-autoport-inferior)))) (if p (delete-process p) nil)) (funcall (jsonrpc--on-shutdown connection) connection)))
  (if (not (process-live-p proc)) (progn (save-current-buffer (set-buffer (jsonrpc-events-buffer connection)) (let ((inhibit-read-only t)) (insert "\n----------b---y---e---b---y---e----------\n"))) (mapc #'(lambda (jsonrpc-lambda-elem167) (apply #'(lambda (_id _method _success-fn _error-fn timer) (if timer (progn (cancel-timer timer)))) jsonrpc-lambda-elem167)) (jsonrpc--continuations connection)) (maphash #'(lambda (_ triplet) (progn (ignore (consp triplet)) (let* ((x919 (cdr-safe triplet))) (progn (ignore (consp x919)) (let* ((x920 (car-safe x919)) (x921 (cdr-safe x919))) (progn (ignore (consp x921)) (let* ((x923 (cdr-safe x921))) (progn (ignore (null x923)) (let ((timer x920)) (if timer (progn ...))))))))))) (jsonrpc--deferred-actions connection)) (process-put proc 'jsonrpc-sentinel-cleanup-started t) (unwind-protect (mapc #'(lambda (jsonrpc-lambda-elem168) (apply #'(lambda (_id _method _success-fn error-fn _timer) (funcall error-fn '(:code -1 :message "Server died"))) jsonrpc-lambda-elem168)) (jsonrpc--continuations connection)) (jsonrpc--message "Server exited with status %s" (process-exit-status proc)) (delete-process proc) (let* ((p (and t (slot-value connection '-autoport-inferior)))) (if p (delete-process p) nil)) (funcall (jsonrpc--on-shutdown connection) connection))))
  (let ((connection (process-get proc 'jsonrpc-connection))) (jsonrpc--debug connection "Connection state change: `%s'" change) (if (not (process-live-p proc)) (progn (save-current-buffer (set-buffer (jsonrpc-events-buffer connection)) (let ((inhibit-read-only t)) (insert "\n----------b---y---e---b---y---e----------\n"))) (mapc #'(lambda (jsonrpc-lambda-elem167) (apply #'(lambda (_id _method _success-fn _error-fn timer) (if timer (progn (cancel-timer timer)))) jsonrpc-lambda-elem167)) (jsonrpc--continuations connection)) (maphash #'(lambda (_ triplet) (progn (ignore (consp triplet)) (let* ((x919 (cdr-safe triplet))) (progn (ignore (consp x919)) (let* ((x920 (car-safe x919)) (x921 (cdr-safe x919))) (progn (ignore (consp x921)) (let* ((x923 (cdr-safe x921))) (progn (ignore (null x923)) (let (...) (if timer ...)))))))))) (jsonrpc--deferred-actions connection)) (process-put proc 'jsonrpc-sentinel-cleanup-started t) (unwind-protect (mapc #'(lambda (jsonrpc-lambda-elem168) (apply #'(lambda (_id _method _success-fn error-fn _timer) (funcall error-fn '(:code -1 :message "Server died"))) jsonrpc-lambda-elem168)) (jsonrpc--continuations connection)) (jsonrpc--message "Server exited with status %s" (process-exit-status proc)) (delete-process proc) (let* ((p (and t (slot-value connection '-autoport-inferior)))) (if p (delete-process p) nil)) (funcall (jsonrpc--on-shutdown connection) connection)))))
  jsonrpc--process-sentinel(#<process EGLOT (dune-add-disable-file-watcher-rpc/(tuareg-mode))> "exited abnormally with code 1\n")