`vim.lsp.start` Failure For `ast-grep` Language Server When Using Sessions And `autochdir`

by ADMIN 91 views

Problem

The ast-grep language server fails to start when using root_markers but works fine if a custom (and equivalent) root_dir function is provided. This issue seems to only appear when using autochdir and a session file where the session is restored to a buffer that is not in a project that does not contain the root marker file, and a buffer in the session is switched to that is in a project containing the root marker file.

Error Message

The error seen is:

Client ast_grep quit with exit code 2 and signal 0. Check log for errors: /Users/josh/.local/state/nvim/lsp.log

and the lsp.log has the error:

[ERROR][2025-05-17 13:57:22] ...p/_transport.lua:36	"rpc"	"ast-grep"	"stderr"	"Error: No ast-grep project configuration is found.\nHelp: You need to create an ast-grep project for this command. Try `sg new` to create one.\nSee also: https://ast-grep.github.io/guide/scan-project.html\n\n"

even though there is an sgconfig.yml (one of the root_markers) present for the project.

Workaround

Manually adding a root_dir function, like:

local make_root_dir = function(root_markers)
  local root_dir = function(bufnr, on_dir)
    on_dir(vim.fs.root(bufnr, root_markers))
  end
  return root_dir
end

vim.lsp.config(
  'ast_grep',
  {
    root_dir = make_root_dir({'sgconfig.yml', 'sgconfig.yaml'})
  }
)
vim.lsp.enable('ast_grep')

works around the problem.

Steps to Reproduce

Setup

Create the following files somewhere (e.g. /tmp/nvim-test)

minimal.lua

for name, url in pairs {
  "https://github.com/neovim/nvim-lspconfig",
} do
  local install_path = vim.fn.fnamemodify('nvim_issue/' .. name, ':p')
  if vim.fn.isdirectory(install_path) == 0 then
    vim.fn.system { 'git', 'clone', '--depth=1', url, install_path }
  end
  vim.opt.runtimepath:append(install_path)
end

local make_root_dir = function(root_markers)
  local root_dir = function(bufnr, on_dir)
    on_dir(vim.fs.root(bufnr, root_markers))
  end
  return root_dir
end

-- uncomment to work around issue:
-- vim.lsp.config(
--   'ast_grep',
--   {
--     root_dir = make_root_dir(vim.lsp.config.ast_grep.root_markers)
--   }
-- )
vim.lsp.enable({ 'ast_grep' })

sgconfig.yml

Basic config for ast-grep:

ruleDirs:
  - rules

foo.py

A dummy file to open:

def foo():
    pass

Steps to Reproduce

  1. Install ast-grep:

    e.g. for macOS:

    $ brew install ast-grep
    
  2. Start nvim with the minimal configuration:

    $ cd /tmp/nvim-test
    $ nvim --clean -u /tmp/nvim-test/minimal.lua
    
  3. Open foo.py (i.e. :e /tmp/nvim-test/foo.py)

  4. Open and save some file outside of the project (e.g. :e /tmp/bar.py | :w)

  5. Save the session :mksession /tmp/Session.vim and quit.

  6. Restart nvim and load the saved session:

    $ nvim --clean -u /tmp/nvim-test/minimal.lua -S /tmp/Session.vim
    
  7. Switch to foo.py (i.e. :b foo.py or :b #)

The error should now appear. Uncommenting the vim.lsp.config(...) in minimal.lua should prevent the error.

Expected Behavior

The language server should start without errors.

Nvim Version (nvim -v)

v0.12.0-nightly+2fda267

Vim (not Nvim) Behaves the Same?

n/a

Operating System/Version

macOS 15.5

Terminal Name/Version

alacritty 0.15.1 (0c405d5)

$TERM Environment Variable

tmux-256color

Installation

Nix neovim nightly overlay: https://github.com/nix-community/neovim-nightly-overlay

Possible Cause

The issue seems to be related to the way root_dir is specified. When root_dir is specified as a function, the server is being initialised using vim.schedule, but without root_dir it is initialised immediately and there is some race-condition.

Solution

To fix this issue, we need to ensure that the root_dir function is called before the language server is initialised. We can do this by adding a vim.schedule call to the root_dir function.

local make_root_dir = function(root_markers)
  local root_dir = function(bufnr, on_dir)
    vim.schedule(function()
      on_dir(vim.fs.root(bufnr, root_markers))
    end)
  end
  return root_dir
end

vim.lsp.config(
  'ast_grep',
  {
    root_dir = make_root_dir({'sgconfig.yml', 'sgconfig.yaml'})
  }
)
vim.lsp.enable('ast_grep')

Q: What is the issue with the ast-grep language server when using sessions and autochdir?

A: The issue is that the language server fails to start when using root_markers but works fine if a custom (and equivalent) root_dir function is provided. This issue seems to only appear when using autochdir and a session file where the session is restored to a buffer that is not in a project that does not contain the root marker file, and a buffer in the session is switched to that is in a project containing the root marker file.

Q: What is the error message that is seen when the issue occurs?

A: The error seen is:

Client ast_grep quit with exit code 2 and signal 0. Check log for errors: /Users/josh/.local/state/nvim/lsp.log

and the lsp.log has the error:

[ERROR][2025-05-17 13:57:22] ...p/_transport.lua:36	"rpc"	"ast-grep"	"stderr"	"Error: No ast-grep project configuration is found.\nHelp: You need to create an ast-grep project for this command. Try `sg new` to create one.\nSee also: https://ast-grep.github.io/guide/scan-project.html\n\n"

even though there is an sgconfig.yml (one of the root_markers) present for the project.

Q: What is the workaround for this issue?

A: Manually adding a root_dir function, like:

local make_root_dir = function(root_markers)
  local root_dir = function(bufnr, on_dir)
    on_dir(vim.fs.root(bufnr, root_markers))
  end
  return root_dir
end

vim.lsp.config(
  'ast_grep',
  {
    root_dir = make_root_dir({'sgconfig.yml', 'sgconfig.yaml'})
  }
)
vim.lsp.enable('ast_grep')

works around the problem.

Q: What are the steps to reproduce this issue?

A: The steps to reproduce this issue are:

  1. Install ast-grep:

    e.g. for macOS:

    $ brew install ast-grep
    
  2. Start nvim with the minimal configuration:

    $ cd /tmp/nvim-test
    $ nvim --clean -u /tmp/nvim-test/minimal.lua
    
  3. Open foo.py (i.e. :e /tmp/nvim-test/foo.py)

  4. Open and save some file outside of the project (e.g. :e /tmp/bar.py | :w)

  5. Save the session :mksession /tmp/Session.vim and quit.

  6. Restart nvim and load the saved session:

    $ nvim --clean -u /tmp/nvim-test/minimal.lua -S /tmp/Session.vim
    
  7. Switch to foo.py (i.e. :b foo.py or :b #)

The error should now appear. Uncommenting the vim.lsp.config(...) in minimal.lua should prevent the error.

Q: What is the expected behavior?

A: The language server should start without errors.

Q: Does this issue occur in Vim (not Nvim)?

A: n/a

Q: What is the operating system and version?

A: macOS 15.5

Q: What is the terminal name and version?

A: alacritty 0.15.1 (0c405d5)

Q: What is the $TERM environment variable?

A: tmux-256color

Q: How was the installation done?

A: Nix neovim nightly overlay: https://github.com/nix-community/neovim-nightly-overlay

Q: What is the possible cause of this issue?

A: The issue seems to be related to the way root_dir is specified. When root_dir is specified as a function, the server is being initialised using vim.schedule, but without root_dir it is initialised immediately and there is some race-condition.

Q: What is the solution to this issue?

A: To fix this issue, we need to ensure that the root_dir function is called before the language server is initialised. We can do this by adding a vim.schedule call to the root_dir function.

local make_root_dir = function(root_markers)
  local root_dir = function(bufnr, on_dir)
    vim.schedule(function()
      on_dir(vim.fs.root(bufnr, root_markers))
    end)
  end
  return root_dir
end

vim.lsp.config(
  'ast_grep',
  {
    root_dir = make_root_dir({'sgconfig.yml', 'sgconfig.yaml'})
  }
)
vim.lsp.enable('ast_grep')

This should fix the issue and allow the language server to start without errors.