Subject: bug#66144: 29.1; eglot-shutdown request params violate JSONRPC spec

From: Aaron Zeng <[email-masked]>
Date: Thu, 21 Sep 2023 16:59:14 -0400

When using Eglot with ocamllsp (or other language servers built with the
same OCaml LSP server library), M-x eglot-shutdown sends an invalid
JSONRPC request to the language server and causes it to exit with an
error message instead of gracefully.  As an additional side effect of
the error, eglot will restart/reconnect to the language server instead
of staying disconnected.

1. emacs -Q
2. C-x C-f ~/temp.ml
3. M-: (setq major-mode 'tuareg-mode)
4. M-x eglot
5. C-u M-x eglot-shutdown

This produces the following *Messages*:

    [eglot] Connected! Server `ocamllsp' now managing `(tuareg-mode caml-mode reason-mode)' buffers in project `azeng'.
    [eglot] Asking EGLOT (azeng/(tuareg-mode caml-mode reason-mode)) politely to terminate
    [jsonrpc] Server exited with status 1
    jsonrpc-error: "request id=5 failed:", (jsonrpc-error-code . -1), (jsonrpc-error-message . "Server died"), (jsonrpc-error-data)

And from the events buffer:

    [client-request] (id:2) Thu Sep 21 16:48:11 2023:
    (:jsonrpc "2.0" :id 2 :method "shutdown" :params nil)
    [stderr] (monitor.ml.Error
    [stderr]  ("Jsonrpc: json conversion failed: invalid structured value")
    [stderr]  ("Raised at Lsp_json_rpc_types__Import.Json.error in file \"import.ml\" (inlined), line 31, characters 23-50"
    [stderr]   "Called from Lsp_json_rpc_types__Jsonrpc.Structured.t_of_yojson in file \"jsonrpc.ml\" (inlined), line 59, characters 14-56"
    [stderr]   "Called from Lsp_json_rpc_types__Import.Option.map in file \"import.ml\" (inlined), line 7, characters 21-26"
    [stderr]   "Called from Lsp_json_rpc_types__Import.Json.field in file \"import.ml\" (inlined), line 46, characters 31-79"
    [stderr]   "Called from Lsp_json_rpc_types__Jsonrpc.Packet.t_of_fields in file \"jsonrpc.ml\", line 298, characters 21-77"
    [stderr]   "Called from Lsp__Io.Make.read in file \"io.ml\" (inlined), line 108, characters 24-57"
    [stderr]   "Called from Fiber__Core.O.(>>=).(fun) in file \"core.ml\", line 251, characters 34-39"
    [stderr]   "Called from Fiber__Scheduler.exec in file \"scheduler.ml\", line 76, characters 8-11"
    [stderr]   "Re-raised at Stdune__Exn.raise_with_backtrace.(partial) in file \"exn.ml\" (inlined), line 38, characters 27-56"
    [stderr]   "Called from Stdune__Exn_with_backtrace.reraise in file \"exn_with_backtrace.ml\" (inlined), line 20, characters 33-71"
    [stderr]   "Called from Fiber__Scheduler.exec in file \"scheduler.ml\", line 81, characters 30-60"
    [stderr]   "Called from Fiber__Scheduler.advance in file \"scheduler.ml\" (inlined), line 194, characters 2-58"
    [stderr]   "Called from Fiber_async.deferred_of_fiber.loop.(fun) in file \"fiber_async.ml\" (inlined), line 44, characters 11-53"))
    [internal] Thu Sep 21 16:48:11 2023:
    (:message "Connection state changed" :change "exited abnormally with code 1\n")
    
    ----------b---y---e---b---y---e----------

It seems that jsonrpc.el is sending a "null" value for the params field
of a request, when it should instead be omitting the field or sending an
empty JSON object instead:
https://www.jsonrpc.org/specification#parameter_structures

Users of other editors have reported similar issues, and concluded that
this was correct (if pedantic) behavior by ocamllsp and should be fixed
in the editor: https://github.com/helix-editor/helix/issues/5400

In GNU Emacs 29.1 (build 1, x86_64-pc-linux-gnu, X toolkit, cairo
 version 1.15.12, Xaw scroll bars) of 2023-09-21 built on
 igm-qws-u12685a
Repository revision: a9b28224af0f73d1fe0f422e9b318c5b91af889b
Repository branch: HEAD
Windowing system distributor 'The X.Org Foundation', version 11.0.12011000
System Description: Rocky Linux 8.8 (Green Obsidian)

Configured using:
 'configure --with-x-toolkit=lucid --without-gpm --without-gconf
 --without-selinux --without-imagemagick --with-modules --with-gif=no
 --with-cairo --with-rsvg'

Configured features:
CAIRO DBUS FREETYPE GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG JSON
LIBSYSTEMD LIBXML2 MODULES NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND
SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS X11 XDBE XIM XINPUT2 XPM LUCID
ZLIB

Important settings:
  value of $LANG: en_US.utf8
  locale-coding-system: utf-8-unix

Major mode: Fundamental

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  blink-cursor-mode: t
  buffer-read-only: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(tabify cus-edit cus-start cus-load wid-edit cl-print mailalias rmail
shadow sort mail-extr emacsbug message mailcap yank-media puny dired
dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev
gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util
mail-prsvr mail-utils time-date cl-extra eglot external-completion array
filenotify jsonrpc ert pp ewoc debug backtrace find-func xref
flymake-proc flymake warnings icons compile text-property-search comint
ansi-osc ansi-color ring pcase url-util url-parse auth-source cl-seq
eieio eieio-core cl-macs password-cache json subr-x map url-vars project
byte-opt gv bytecomp byte-compile imenu thingatpt help-fns radix-tree
help-mode cl-loaddefs cl-lib rmc iso-transl tooltip cconv eldoc paren
electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode mwheel
term/x-win x-win term/common-win x-dnd tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode
prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu
timer select scroll-bar mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads dbusbind inotify
dynamic-setting system-font-setting font-render-setting cairo x-toolkit
xinput2 x multi-tty make-network-process emacs)

Memory information:
((conses 16 114174 16392)
 (symbols 48 11887 5)
 (strings 32 36275 1576)
 (string-bytes 1 1007705)
 (vectors 16 22858)
 (vector-slots 8 309640 13918)
 (floats 8 79 204)
 (intervals 56 351 0)
 (buffers 976 20))