Skip to content

Do not compile when loading formatter plugins in non mix projects - #1279

Merged
lukaszsamson merged 1 commit into
masterfrom
fix-formatter-compile-in-server
Oct 18, 2026
Merged

lukaszsamson merged 1 commit into
masterfrom
fix-formatter-compile-in-server

Conversation

@lukaszsamson

Copy link
Copy Markdown
Collaborator

Crash

Reported on Elixir 1.19.5:

** (FunctionClauseError) no function clause matching in ElixirLS.LanguageServer.Server.handle_info/2
  server.ex:391: ElixirLS.LanguageServer.Server.handle_info({#Reference<...>, {:error, [%{message: "** (MatchError) no match of right hand side value: {:error, :enoent} ... (elixir 1.19.5) Kernel.ParallelCompiler spawn_workers ...", stacktrace: [{:elixir_compiler, :file, 2, ...}], severity: :error, ...}], %{runtime_warnings: [], compile_warnings: [%{message: "redefining module Jason.Codegen ...", ...}, ...]}}}, %ElixirLS.LanguageServer.Server{...})

Root cause

The {ref, {:error, errors, %{runtime_warnings: ..., compile_warnings: ...}}} message is the final reply Mix's Elixir compiler worker sends to the process that called Mix.Task.run("compile"). So a compile ran inside the Server process, got aborted, and its late reply landed in the Server mailbox.

SourceFile.formatter_for/4 passes a non-compiling plugin_loader in the mix project branch, but not in the non mix project branch. There Mix.Tasks.ElixirLSFormat.formatter_for_file/2 falls back to its default loader, which runs mix loadpaths and mix compile whenever .formatter.exs lists plugins. The completion handler calls formatter_for/4 synchronously in the Server process, and the surrounding catch swallowed the aborted compile, leaving the stray message behind.

Fix

  • Factor the non-compiling plugin loader out into SourceFile.plugin_loader/1 and use it in both branches. Plugins that are not loaded are skipped with a warning, the same as in mix projects.
  • Add a catch-all handle_info/2 to ElixirLS.LanguageServer.Server that logs and ignores unexpected messages, so a stray message can no longer bring down the server.

Tests

  • formatter_for/4 in a non mix project whose .formatter.exs lists one loaded and one missing plugin: it returns options, the loaded plugin is used and no compile runs. Before this change it went through the Mix loader and returned {:error, "Formatter plugin ... cannot be found"}.
  • The Server survives unexpected messages, including a compiler worker style reply.

SourceFile.formatter_for/4 passed the non-compiling plugin loader only in
the mix project branch. In the non mix branch the default ElixirLSFormat
plugin loader ran mix loadpaths and mix compile in the calling process
(the Server GenServer for completion requests). An aborted compile left the
compiler worker reply in the Server mailbox and crashed it with a
FunctionClauseError in handle_info/2.

Use the same non-compiling plugin loader in both branches and add a
catch-all handle_info/2 to the Server that logs and ignores unexpected
messages.
@lukaszsamson
lukaszsamson merged commit b2ea746 into master Oct 18, 2026
23 checks passed
@lukaszsamson
lukaszsamson deleted the fix-formatter-compile-in-server branch October 18, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant