Looking for Emacs users to help me debug an issue with Emacs, eglot, and jetls

I ran into an issue with either Emacs, eglot, or jetls. @aviatesk was unable to reproduce it, so I made a minimal reproducible environment with straight.el at

I wonder if anyone can reproduce the issue running from ./run.sh, and if file.jl is otherwise fine in your standard config, how the versions of the relevant packages differ.

I really want to use jetls but there may be an issue lurking in my setup that I need to track down to make the experience hassle-free.

I can reproduce it

Thanks for putting together the MWE.
I dug into this and the eglot error message itself is a strong signal:

(args-out-of-range “inplace_∂g∂x_v!(x)” 21 22)

The string “inplace_∂g∂x_v!(x)” has:

  • 18 characters
  • 22 bytes in UTF-8 (each takes 3 bytes)
  • 18 UTF-16 code units (∂ is in the BMP)

The args 21 22 only match the UTF-8 byte length. Emacs string indexing is character-based, so accessing index 21–22 in an 18-character string blows up. So eglot has a code path that’s receiving UTF-8 byte offsets but using them as Emacs character positions without converting. That a timer fires it suggests something in the eldoc / hover-related-info display.

What JETLS does: it checks general.positionEncodings from the client and prefers UTF-8 if advertised (since UTF-8 is the native encoding for Julia strings — no conversion needed server-side), falling back to UTF-16 otherwise. eglot 1.17+ advertises UTF-8, so JETLS picks UTF-8 when talking to it.
VS Code and Zed both handle the same UTF-8 positions correctly, which is consistent with this being a bug somewhere in eglot’s processing path rather than in the data JETLS sends.

You can verify this on your setup by forcing JETLS to use UTF-16. Set up a local JETLS checkout with this patch applied to JETLS:

diff --git a/src/initialize.jl b/src/initialize.jl
index 4ba882ae..022725cc 100644
--- a/src/initialize.jl
+++ b/src/initialize.jl
@@ -261,14 +261,15 @@ function handle_InitializeRequest(
         end
     end
 
-    positionEncodings = getcapability(state, :general, :positionEncodings)
-    if isnothing(positionEncodings) || isempty(positionEncodings)
-        positionEncoding = PositionEncodingKind.UTF16
-    elseif PositionEncodingKind.UTF8 in positionEncodings
-        positionEncoding = PositionEncodingKind.UTF8
-    else
-        positionEncoding = first(positionEncodings)
-    end
+    # positionEncodings = getcapability(state, :general, :positionEncodings)
+    # if isnothing(positionEncodings) || isempty(positionEncodings)
+    #     positionEncoding = PositionEncodingKind.UTF16
+    # elseif PositionEncodingKind.UTF8 in positionEncodings
+    #     positionEncoding = PositionEncodingKind.UTF8
+    # else
+    #     positionEncoding = first(positionEncodings)
+    # end
+    positionEncoding = PositionEncodingKind.UTF16
     state.encoding = positionEncoding
 
     result = InitializeResult(;

If the error goes away, that confirms the issue lives on the eglot side, and we can take it upstream from there.

Thanks for looking into this, and the hint above. Unfortunately, with the patch above I run into another error, not being able to start jsonrpc:

Debugger entered--Lisp error: (error "[jsonrpc] Could not start and/or connect")
  error("[jsonrpc] Could not start and/or connect")
  #f(compiled-function () #<bytecode 0x162a403f347a5cc3>)()
  #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)(#<eglot-lsp-server eglot-lsp-server-1e0112a0638f>)
  #f(compiled-function (conn slots) #<bytecode 0x11900d17e27cb127>)(#<eglot-lsp-server eglot-lsp-server-1e0112a0638f> (:name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)))
  apply(#f(compiled-function (conn slots) #<bytecode 0x11900d17e27cb127>) (#<eglot-lsp-server eglot-lsp-server-1e0112a0638f> (:name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>))))
  #f(compiled-function (&rest args) #<bytecode -0x10f10a0df0dd61f4>)(#<eglot-lsp-server eglot-lsp-server-1e0112a0638f> (:name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)))
  apply(#f(compiled-function (&rest args) #<bytecode -0x10f10a0df0dd61f4>) #<eglot-lsp-server eglot-lsp-server-1e0112a0638f> (:name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)))
  initialize-instance(#<eglot-lsp-server eglot-lsp-server-1e0112a0638f> (:name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)))
  #f(compiled-function (class &rest slots) "Default constructor for CLASS `eieio-default-superclass'.\nSLOTS are the initialization slots used by `initialize-instance'.\nThis static method is called when an object is constructed.\nIt allocates the vector used to represent an EIEIO object, and then\ncalls `initialize-instance' on that object." #<bytecode 0x9edf507a1ea8541>)(eglot-lsp-server :name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>))
  apply(#f(compiled-function (class &rest slots) "Default constructor for CLASS `eieio-default-superclass'.\nSLOTS are the initialization slots used by `initialize-instance'.\nThis static method is called when an object is constructed.\nIt allocates the vector used to represent an EIEIO object, and then\ncalls `initialize-instance' on that object." #<bytecode 0x9edf507a1ea8541>) eglot-lsp-server (:name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)))
  make-instance(eglot-lsp-server :name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown :process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>))
  apply(make-instance eglot-lsp-server :name "EGLOT (jetls-mwe/(julia-mode julia-ts-mode))" :events-buffer-config (:size 2000000 :format full) :notification-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80f82948aa>) :request-dispatcher #f(compiled-function (server method params) #<bytecode 0x1f2ccd80e62948aa>) :on-shutdown eglot--on-shutdown (:process #f(compiled-function (conn) #<bytecode -0x12ca85559dddb8ac>)))
  eglot--connect((julia-mode julia-ts-mode) (vc Git "~/code/elisp/jetls-mwe/") eglot-lsp-server ("/home/tamas/.juliaup/bin/julia +1.12 --startup-file=no --project=/home/tamas/src/JETLS.jl -m JETLS serve" "serve" "--socket" :autoport) ("julia" "julia"))
  apply(eglot--connect ((julia-mode julia-ts-mode) (vc Git "~/code/elisp/jetls-mwe/") eglot-lsp-server ("/home/tamas/.juliaup/bin/julia +1.12 --startup-file=no --project=/home/tamas/src/JETLS.jl -m JETLS serve" "serve" "--socket" :autoport) ("julia" "julia")))
  #f(compiled-function () #<bytecode -0x12ad5ac15af6be45>)()

In any case I opened an eglot issue at

Those of you who know about the intricacies of the protocol should maybe comment there, I guess it is rare for languages to support Unicode natively so Julia may testing code paths that were previously unused.

Addendum: apparently before LanguageServer 3.17, the only encoding option was UTF16.

@aviatesk, in the original issue you said that you cannot replicate, so there must be something different in your config compared to the MWE in the repo. If you could tell me what it is, I could use that as a workaround.

When I run your script using emacs 30.2, I do not encounter the error, only a minor warning from flymake. Perhaps noteworthy is that the eglot events buffer shows ∂ in the function name as three octal bytes rather than the unicode glyph.
Edit: I am using jetls version 2026-05-06.

You mean that when you go to the relevant line (line 6), to display the entire message, it works OK?

If yes, please share the details of your config, ie the relevant package versions for

  • eldoc
  • external-completion
  • flymake
  • jsonrpc
  • project
  • seq
  • xref

and also your eglot/jetls setup customizations, if any.

Since not everyone is able to replicate this bug, I suspect that there is a configuration that works.

I am using the straight setup from your MWE repo for this test. When I try it again today, I do encounter the indexing error when the cursor is over the function name on line 6; I don’t understand why I didn’t see it yesterday. Sorry for the noise if I simply didn’t test it correctly.

Anyway, you can obviate this particular error by adding :signatureHelpProvider to the eglot-ignored-server-capabilities variable (which is accessible from the emacs customization menu).

Also, “[jsonrpc] Could not start and/or connect” may just be a timeout problem. If so, you can manually re-invoke M-x eglot for the buffer, and jetls will restart more quickly (with cached library files, etc.)