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.