Stream: beginners

Topic: `roc experimental-lsp` crashes; bug in LSP or my code?


view this post on Zulip Lukas Juhrich (Jan 09 2026 at 18:58):

I tried to get acquainted with the new compiler, new syntax, and the language in general. I've been able to run this (yay!) with roc build (@5f964dcd):

app [main!] { pf: platform "https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.5/BJBzo2SR2o5w3StmubGWvnPHq6hfePMaNWy5MwkPuZUs.tar.zst" }

import pf.Stdout
import pf.Arg exposing [Arg]

# main! : List Arg => Try {}
main! = |_args| {
    Stdout.line!("Hello, World!")
    dbg 5->calc()
    Stdout.line!("${calc(7).to_str()}")
    Ok({})
}

calc = |n| n + 1

trying to set up the LSP in helix however, I get a crash when viewing this file:

thread 27907 panic: Arrays out of sync:
 type_nodes=17
  region_nodes=38

/home/lukas/code/30software/roc/src/check/Check.zig:255:28: 0x4be3638 in copyVar (mod.zig)
            std.debug.panic(
                           ^
/home/lukas/code/30software/roc/src/check/Check.zig:1260:57: 0x4965ed8 in checkPlatformRequirements (mod.zig)
            const copied_required_var = try self.copyVar(required_type_var, platform_env, required_type.region);
                                                        ^
/home/lukas/code/30software/roc/src/compile/compile_build.zig:683:46: 0x4a76d82 in checkPlatformRequirements (mod.zig)
        try checker.checkPlatformRequirements(platform_root_env, &platform_to_app_idents);
                                             ^
/home/lukas/code/30software/roc/src/compile/compile_build.zig:586:43: 0x4a7bfa5 in build (mod.zig)
        try self.checkPlatformRequirements();
                                          ^
/home/lukas/code/30software/roc/src/lsp/syntax.zig:88:18: 0x4dfecec in check (mod.zig)
        env.build(absolute_path) catch |err| {
                 ^
/home/lukas/code/30software/roc/src/lsp/server.zig:236:63: 0x4e061d7 in runSyntaxCheck (mod.zig)
            const publish_sets = try self.syntax_checker.check(uri, if (doc) |d| d.text else null, root_path);
                                                              ^
/home/lukas/code/30software/roc/src/lsp/server.zig:225:32: 0x4e06bce in onDocumentChanged (mod.zig)
            self.runSyntaxCheck(uri) catch |err| {
                               ^
/home/lukas/code/30software/roc/src/lsp/handlers/did_open.zig:44:35: 0x4a2666d in call (mod.zig)
            self.onDocumentChanged(uri);
                                  ^
/home/lukas/code/30software/roc/src/lsp/server.zig:176:24: 0x4832b25 in handleNotification (mod.zig)
                handler(self, params) catch |err| {
                       ^
/home/lukas/code/30software/roc/src/lsp/server.zig:149:44: 0x4833850 in handlePayload (mod.zig)
                try self.handleNotification(method, obj.get(\params\"));\
                                           ^
/home/lukas/code/30software/roc/src/lsp/server.zig:114:31: 0x4833ec3 in processNextMessage (mod.zig)
            self.handlePayload(payload) catch |err| {
                              ^
/home/lukas/code/30software/roc/src/lsp/server.zig:100:47: 0x48341a4 in run (mod.zig)
            while (try self.processNextMessage()) {}
                                              ^
/home/lukas/code/30software/roc/src/lsp/server.zig:312:19: 0x4834b40 in runWithStdIo (mod.zig)
    try server.run();
                  ^
/home/lukas/code/30software/roc/src/lsp/mod.zig:10:28: 0x4834ffd in runWithStdIo (mod.zig)
    try server.runWithStdIo(allocator, debug);
                           ^
/home/lukas/code/30software/roc/src/cli/main.zig:766:61: 0x4893ac9 in mainArgs (main.zig)
        .experimental_lsp => |lsp_args| try lsp.runWithStdIo(allocs.gpa, .{
                                                            ^
/home/lukas/code/30software/roc/src/cli/main.zig:643:13: 0x4895408 in main (main.zig)
    mainArgs(&allocs, args) catch |err| {
            ^
/usr/lib/zig/std/start.zig:627:37: 0x4895a01 in main (std.zig)
            const result = root.main() catch |err| {
                                    ^
/usr/lib/zig/libc/musl/src/env/__libc_start_main.c:95:7: 0x9723e9b in libc_start_main_stage2 (/usr/lib/zig/libc/musl/src/env/__libc_start_main.c)
 exit(main(argc, argv, envp));
      ^
???:?:?: 0x9702149 in ??? (???)
Unwind error at address `exe:0x9702149` (error.MissingFDE), trace may be incomplete

This is the LSP communication to that point:

2026-01-09T19:36:48.612 helix_core::syntax [INFO] Skipping syntax config for 'roc' because the parser's shared library does not exist
2026-01-09T19:36:48.613 helix_lsp::transport [INFO] roc-lsp -> {"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{"general":{"positionEncodings":["utf-8","utf-32","utf-16"]},"textDocument":{"codeAction":{"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"dataSupport":true,"disabledSupport":true,"isPreferredSupport":true,"resolveSupport":{"properties":["edit","command"]}},"completion":{"completionItem":{"deprecatedSupport":true,"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"snippetSupport":true,"tagSupport":{"valueSet":[1]}},"completionItemKind":{}},"formatting":{"dynamicRegistration":false},"hover":{"contentFormat":["markdown"]},"inlayHint":{"dynamicRegistration":false},"publishDiagnostics":{"tagSupport":{"valueSet":[1,2]},"versionSupport":true},"rename":{"dynamicRegistration":false,"honorsChangeAnnotations":false,"prepareSupport":true},"signatureHelp":{"signatureInformation":{"activeParameterSupport":true,"documentationFormat":["markdown"],"parameterInformation":{"labelOffsetSupport":true}}}},"window":{"workDoneProgress":true},"workspace":{"applyEdit":true,"configuration":true,"didChangeConfiguration":{"dynamicRegistration":false},"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":false},"executeCommand":{"dynamicRegistration":false},"fileOperations":{"didRename":true,"willRename":true},"inlayHint":{"refreshSupport":false},"symbol":{"dynamicRegistration":false},"workspaceEdit":{"documentChanges":true,"failureHandling":"abort","normalizesLineEndings":false,"resourceOperations":["create","rename","delete"]},"workspaceFolders":true}},"clientInfo":{"name":"helix","version":"25.07.1"},"processId":27898,"rootPath":"/home/lukas/code/30software/roc","rootUri":"file:///home/lukas/code/30software/roc","workspaceFolders":[{"name":"roc","uri":"file:///home/lukas/code/30software/roc"}]},"id":0}
2026-01-09T19:36:48.619 helix_lsp::transport [ERROR] roc-lsp err <- "roc-lsp logging to /tmp/roc-lsp-debug.log\n"
2026-01-09T19:36:48.622 helix_lsp::transport [INFO] roc-lsp <- {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":2}},"serverInfo":{"name":"roc-lsp","version":"0.1"}}}
2026-01-09T19:36:48.622 helix_lsp::transport [INFO] roc-lsp -> {"jsonrpc":"2.0","method":"initialized","params":{}}
2026-01-09T19:36:48.622 helix_lsp::transport [INFO] roc-lsp -> {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"languageId":"roc","text":"app [main!] { pf: platform \"https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.5/BJBzo2SR2o5w3StmubGWvnPHq6hfePMaNWy5MwkPuZUs.tar.zst\" }\n\nimport pf.Stdout\nimport pf.Arg exposing [Arg]\n\n# main! : List Arg => Try {}\nmain! = |_args| {\n\tStdout.line!(\"Hello, World!\")\n\tdbg 5->calc()\n\tStdout.line!(\"${calc(7).to_str()}\")\n\tOk({})\n}\n\ncalc = |n| n + 1\n","uri":"file:///home/lukas/code/30software/roc/test.roc","version":0}}}
2026-01-09T19:36:48.836 helix_lsp::transport [ERROR] roc-lsp err <- "thread 27907 panic: Arrays out of sync:\n"
[…rest of above stack trace]

Is this a bug in the LSP, nonsense in my attempt at writing roc, or do I need to do some special setup to make the LSP work?

If the first, how can I debug it further?
Thanks in advance :)

view this post on Zulip Luke Boswell (Jan 09 2026 at 20:30):

I noticed your using version 0.5 of the platform, does it still happen with the latest which I think is 0.6?

view this post on Zulip Luke Boswell (Jan 09 2026 at 20:31):

Either way you have definitely found a bug, if you wouldn't mind making a GH issue that'd be good :grinning:

view this post on Zulip Lukas Juhrich (Jan 09 2026 at 20:33):

Alrighty, will check with 0.6. thanks for the quick reaction. :)
Before reporting I'll move around a bit in gdb which I finally managed to set up

view this post on Zulip Lukas Juhrich (Jan 09 2026 at 20:37):

Yep, same behavior with

app [main!] { pf: platform "https://github.com/lukewilliamboswell/roc-platform-template-zig/releases/download/0.6/2BfGn4M9uWJNhDVeMghGeXNVDFijMfPsmmVeo6M4QjKX.tar.zst" }

(note to self: timestamp 2026-01-09T21:36:45.221 in helix logs)

view this post on Zulip Luke Boswell (Jan 09 2026 at 20:38):

I know that feature is super early. We haven't touched it in a while and I don't think we ever got any integration tests for it, so it definitely needs some love.

view this post on Zulip Luke Boswell (Jan 09 2026 at 20:40):

Though that bug is deep in Check which should be quite reliable so I'm guessing some simple is broken here, like how its wired up to the existing compiler pipeline or something.

view this post on Zulip Lukas Juhrich (Jan 09 2026 at 20:40):

No worries, I got that from the experimental. I figured it might be in a „it works but only on tuesdays and for even-length input messages“ kinda situation where I just would've had to know what knobs to turn. happy to report anything

view this post on Zulip Lukas Juhrich (Jan 11 2026 at 19:06):

Wanted to give debugging this myself a shot (via zig build roc && ./zig-out/bin/roc experimental-lsp < crash.lsp), but I struggle in two orthogonal ways:

  1. gdb gets symbols (i.e., correctly shows function names in bt), but has no line info. Am I missing gdb config or a build flag?
  2. building roc takes quite a bit of time, e.g. addidng a self.debugAssertArraysInSync() somewhere in Check.zig triggers a rebuild taking ~1m25s on my machine. That's a bit too long of a loop for printf debugging. A big chunk of that seems to be building interpreter shims for all sorts of platforms (arm, wasm, …) and linking in general. Is there any way I can restrict what gets build, are there any other knobs I can turn, or is my machine just too slow (i7-8650U)? I haven't found any cross-compilation related build flag in zig build --help.

Perhaps I'm missing an obviously better workflow here.

view this post on Zulip Lukas Juhrich (Jan 11 2026 at 19:19):

To give some detail regarding the missing line info: zig itself properly reports lines in the panic:

 ./zig-out/bin/roc experimental-lsp --debug-build --debug-transport --debug-syntax --debug-server < crash.lsp
roc-lsp logging to /tmp/roc-lsp-debug.log
Content-Length: 177

{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":2}},"serverInfo":{"name":"roc-lsp","version":"0.1"}}}thread 203073 panic: [PRE check platform reqs] Arrays out of sync:
 type_nodes=0
  region_nodes=38

/home/lukas/code/30software/roc/src/check/Check.zig:255:28: 0x4965751 in checkPlatformRequirements (mod.zig)
            std.debug.panic(
                           ^
[…]

but when I start this via gdb ./zig-out/bin/roc, I get:

Reading symbols from ./zig-out/bin/roc...
(gdb) run experimental-lsp < crash.lsp
Starting program: /home/lukas/code/30software/roc/zig-out/bin/roc experimental-lsp < crash.lsp
[New LWP 203790]
[LWP 203790 exited]
Content-Length: 177

{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":2}},"serverInfo":{"name":"roc-lsp","version":"0.1"}}}thread 203685 panic: [PRE check platform reqs] Arrays out of sync:
 type_nodes=0
  region_nodes=38

[…backtrace from zig panic…]
(gdb) f 7
#7  0x0000000004965752 in Check.checkPlatformRequirements (self=0x7ffffffece60, platform_env=0x7ffff62e4f20, platform_to_app_idents=0x7ffffffee1d8)
(gdb) list
39      }
40
41      void __restore_sigs(void *set)
42      {
43              __syscall(SYS_rt_sigprocmask, SIG_SETMASK, set, 0, _NSIG/8);
44      }

which is clearly not the source. I don't know how this is supposed to work under the hood, I'm just used to it „just working“ in the gcc -g world.

view this post on Zulip Lukas Juhrich (Jan 11 2026 at 19:44):

Luke Boswell said:

Either way you have definitely found a bug, if you wouldn't mind making a GH issue that'd be good :grinning:

https://github.com/roc-lang/roc/issues/8995 now exists


Last updated: Jan 12 2026 at 12:19 UTC