bug#81514: 32.0.50; Eglot messages progress reporter shows errors

Stéphane Marks <[email protected]> Wed, 29 Jul 2026 22:28:04 +0200
Newsgroups gmane.emacs.bugs
Message-ID <CAN+1HbrNi6wDy4FnGzC3zc0kVDFCqSydBrKXhqvR5RZ1MFpZGQ@mail.gmail.com>
On Wed, Jul 29, 2026 at 3:59 PM João Távora <[email protected]> wrote:

> Hi!
>
> If there are confirmed failings in Eglot's usage of progress reporters,
> fixes are welcome.
>
> That said, please tread carefully: said fixes must not endanger (or worsen
> in any way) the backward compatibility of eglot.el all the way to at least
> Emacs 27, where these progress reporters didn't exist or were more
> primitive.  I'd also appreciate if said fixes are succinct in nature, and
> not architecture-changing...
>
> Thanks!
> João
>
>
>
>
>
>
>
>
>
> On Wed, Jul 29, 2026, 20:33 Stéphane Marks <[email protected]> wrote:
>
>> On Wed, Jul 29, 2026 at 2:47 PM Stéphane Marks <[email protected]>
>> wrote:
>>
>>> On Wed, Jul 29, 2026 at 11:17 AM Jacek Świerk <[email protected]>
>>> wrote:
>>>
>>>> I can reproduce it with clangd on the following C++ repository:
>>>> https://github.com/rui314/mold. It doesn't happen every time but often
>>>> enough:
>>>>
>>>> Debugger entered--Lisp error: (wrong-type-argument sequencep 100)
>>>>   ((20 . [1785337541.907988 nil nil "[eglot] mold
>>>> backgroundIndexProgress: indexing..." 1 0.2 nil nil]) 20 100)
>>>>   progress-reporter-do-update((20 . [1785337541.907988 nil nil "[eglot]
>>>> mold backgroundIndexProgress: indexing..." 1 0.2 nil nil]) 100 nil)
>>>>   eglot--reporter-update((20 . [1785337541.907988 nil nil "[eglot] mold
>>>> backgroundIndexProgress: indexing..." 1 0.2 nil nil]) 100 nil)
>>>>   #f(compiled-function (arg1 arg2 arg3 arg4 &rest rest) #<bytecode
>>>> 0xa1216d65848076a>)(#<eglot-lsp-server 1022b7360904>
>>>> "backgroundIndexProgress" 100 nil)
>>>>   #f(compiled-function (arg1 arg2 &rest rest) "Handle $/progress
>>>> notification identified by TOKEN from SERVER." #<bytecode
>>>> -0xf68beacb9f43862>)(#<eglot-lsp-server 1022b7360904> $/progress :token
>>>> "backgroundIndexProgress" :value (:kind "end"))
>>>>   apply(#f(compiled-function (arg1 arg2 &rest rest) "Handle $/progress
>>>> notification identified by TOKEN from SERVER." #<bytecode
>>>> -0xf68beacb9f43862>) #<eglot-lsp-server 1022b7360904> $/progress (:token
>>>> "backgroundIndexProgress" :value (:kind "end")))
>>>>   eglot-handle-notification(#<eglot-lsp-server 1022b7360904> $/progress
>>>> :token "backgroundIndexProgress" :value (:kind "end"))
>>>>   apply(eglot-handle-notification #<eglot-lsp-server 1022b7360904>
>>>> $/progress (:token "backgroundIndexProgress" :value (:kind "end")))
>>>>   #f(compiled-function (server method params) #<bytecode
>>>> 0x1f1b657efb0838aa>)(#<eglot-lsp-server 1022b7360904> $/progress (:token
>>>> "backgroundIndexProgress" :value (:kind "end")))
>>>>   jsonrpc-connection-receive(#<eglot-lsp-server 1022b7360904> (:jsonrpc
>>>> "2.0" :method "$/progress" :params (:token "backgroundIndexProgress" :value
>>>> (:kind "end"))))
>>>>   #f(compiled-function (conn msg) #<bytecode
>>>> 0x4e6153735258fd3>)(#<eglot-lsp-server 1022b7360904> (:jsonrpc "2.0"
>>>> :method "$/progress" :params (:token "backgroundIndexProgress" :value
>>>> (:kind "end"))))
>>>>   apply(#f(compiled-function (conn msg) #<bytecode 0x4e6153735258fd3>)
>>>> (#<eglot-lsp-server 1022b7360904> (:jsonrpc "2.0" :method "$/progress"
>>>> :params (:token "backgroundIndexProgress" :value (:kind "end")))))
>>>>   timer-event-handler([t 27242 5829 902968 nil #f(compiled-function
>>>> (conn msg) #<bytecode 0x4e6153735258fd3>) (#<eglot-lsp-server 1022b7360904>
>>>> (:jsonrpc "2.0" :method "$/progress" :params (:token
>>>> "backgroundIndexProgress" :value (:kind "end")))) nil 894000 nil])
>>>>
>>>> The LSP message that causes it looks like this from the
>>>> (eglot-events-buffer):
>>>> <-- $/progress
>>>> {"jsonrpc":"2.0","method":"$/progress","params":{"token":"backgroundIndexProgress","value":{"kind":"end"}}}
>>>>
>>>> Based on my understanding of Eglot code, Eglot creates a non-numeric
>>>> progress
>>>> reporter because MIN-VALUE and MAX-VALUE arguments are not provided.
>>>> However, it
>>>> looks like Eglot updates the created progress reporter as it were
>>>> numeric:
>>>> (eglot--reporter-update pr pcnt msg)
>>>>
>>>> For the {"kind": "end"} notification, pcnt is set to 100 and msg is
>>>> nil. After
>>>> commit b6849229003, the pulsing reporter forwards (or update-text
>>>> value) as
>>>> UPDATE-TEXT. That evaluates to 100. Then progress-reporter-echo-area
>>>> tries
>>>> (concat (if update-text " " "") update-text) which triggers the shown
>>>> error.
>>>>
>>> I was able to reproduce the issue without eglot and will take a look.
>>>
>>> Coincidentally, and independent of the issue, eglot seems to want a
>>> numeric reporter but instead creates a non-numeric reporter.  eglot-handle-notification
>>> method seems to also be missing the call to progress-reporter-done that
>>> would clear any stateful reporters it's created such as those in
>>> system-taskbar for which a relevant change was made in Tramp recently.  I
>>> can take a stab at those.
>>>
>>
>> Here's a patch for progress-reporter.  I can look into eglot separately
>> if agreed.
>>
>
Something like this patch should work and it is backward compatible to
Emacs 27.  I added a comment about the LSP protocol value range for
percentage and it says we can ignore values outside that range and/or
integer type.  Should we?

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index f701a38ab8b..173a0b87f68 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -2870,9 +2870,17 @@ eglot-handle-notification
     (cl-flet ((fmt (&rest args) (mapconcat #'identity args " "))
               (mkpr (title)
                 (if (eq eglot-report-progress 'messages)
+                    ;;
https://github.com/microsoft/language-server-protocol/blob/b7f5132c95261c0898ae5124e7a91707abc48fcd/_specifications/specification-3-16.md?plain=1#L1455
+                    ;;
+                    ;; The value range is [0, 100].
+                    ;;
+                    ;; The value should be steadily rising. Clients are
+            ;; free to ignore values that are not following this
+            ;; rule.
                     (make-progress-reporter
                      (format "[eglot] %s %s: %s"
-                             (eglot-project-nickname server) token title))
+                             (eglot-project-nickname server) token title)
+                     0 100)
                   (list 'eglot--mode-line-reporter token title)))
               (upd (pcnt msg &optional
                          (pr (gethash token (eglot--progress-reporters
server))))
@@ -2880,7 +2888,14 @@ eglot-handle-notification
                   ((eq (car pr) 'eglot--mode-line-reporter)
                    (setcdr (cddr pr) (list msg pcnt))
                    (force-mode-line-update t))
-                  (pr (eglot--reporter-update pr pcnt msg)))))
+                  (pr (eglot--reporter-update pr pcnt msg))))
+              (done (&optional
+                     (pr (gethash token (eglot--progress-reporters
server))))
+                (cond
+                 ((eq (car pr) 'eglot--mode-line-reporter)
+                  (setcdr (cddr pr) (list "" 100))
+                  (force-mode-line-update t))
+                 (pr (progress-reporter-done pr)))))
       (eglot--dbind ((WorkDoneProgress) kind title percentage message)
value
         (pcase kind
           ("begin"
@@ -2889,6 +2904,7 @@ eglot-handle-notification
                          (eglot--progress-reporters server))))
           ("report" (upd percentage message))
           ("end" (upd (or percentage 100) message)
+           (done)
            (run-at-time 2 nil
                         (lambda ()
                           (remhash token (eglot--progress-reporters
server))))))))))