Skip to content

fix: skip buffers with URI schemes in root_dir#4345

Open
niklaas wants to merge 4 commits intoneovim:masterfrom
niklaas:ignore-fugitive-buffers
Open

fix: skip buffers with URI schemes in root_dir#4345
niklaas wants to merge 4 commits intoneovim:masterfrom
niklaas:ignore-fugitive-buffers

Conversation

@niklaas
Copy link
Copy Markdown

@niklaas niklaas commented Mar 14, 2026

Problem

Some LSP configs implement a custom root_dir function that unconditionally
calls on_dir(project_root or vim.fn.getcwd()). When no project root is found,
this falls back to getcwd(), causing the LSP to attach to any buffer of a
matching filetype including buffers with non-file URI schemes such as
fugitive:// (vim-fugitive).

Once attached, the LSP sends requests using the fugitive:// URI, which is not
a valid file:// URI and causes errors.

The affected configs are ts_ls, tsgo, vtsls, and matlab_ls.

Configs that use root_markers only (e.g. lua_ls, ruby_lsp) are naturally
safe because vim.fs.root() returns nil for non-file buffers, so on_dir is
never called.

Solution

Add a guard at the top of each affected root_dir function that returns early
when the buffer name starts with a URI scheme:

if vim.api.nvim_buf_get_name(bufnr):match('^%a+://') then
    return
end

From my understanding, neovim buffer names are always plain paths (e.g.
/home/user/file.ts) for real files, never file:// URIs, so this guard only
skips genuinely non-file buffers.

I don't think the early returns trigger false positives but for sure let me know if they do.

niklaas added 4 commits March 14, 2026 18:15
Problem:
Buffers with non-file URI schemes (e.g. fugitive://, oil://) have no
real file path. The root_dir function unconditionally calls
on_dir(project_root or vim.fn.getcwd()), which causes ts_ls to attach
to such buffers and error because LSP cannot handle non-file:// URIs.

Solution:
Return early from root_dir when the buffer name starts with a URI
scheme, preventing attachment to buffers that LSP cannot handle.
Problem:
Buffers with non-file URI schemes (e.g. fugitive://, oil://) have no
real file path. The root_dir function unconditionally calls
on_dir(project_root or vim.fn.getcwd()), which causes tsgo to attach
to such buffers and error because LSP cannot handle non-file:// URIs.

Solution:
Return early from root_dir when the buffer name starts with a URI
scheme, preventing attachment to buffers that LSP cannot handle.
Problem:
Buffers with non-file URI schemes (e.g. fugitive://, oil://) have no
real file path. The root_dir function unconditionally calls
on_dir(project_root or vim.fn.getcwd()), which causes vtsls to attach
to such buffers and error because LSP cannot handle non-file:// URIs.

Solution:
Return early from root_dir when the buffer name starts with a URI
scheme, preventing attachment to buffers that LSP cannot handle.
Problem:
Buffers with non-file URI schemes (e.g. fugitive://, oil://) have no
real file path. The root_dir function unconditionally calls
on_dir(root_dir or vim.fn.getcwd()), which causes matlab_ls to attach
to such buffers and error because LSP cannot handle non-file:// URIs.

Solution:
Return early from root_dir when the buffer name starts with a URI
scheme, preventing attachment to buffers that LSP cannot handle.
@niklaas niklaas marked this pull request as ready for review March 14, 2026 17:56
Comment on lines +23 to 27
if vim.api.nvim_buf_get_name(bufnr):match('^%a+://') then
return
end
local root_dir = vim.fs.root(bufnr, '.git')
on_dir(root_dir or vim.fn.getcwd())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clear explanation. But I don't quite see why the solution was chosen.

I think it's more like a hint that getcwd() should not be used as a fallback. People really seem to love it though ...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more like a hint that getcwd() should not be used as a fallback.

Good catch, I agree. I guess I subconsciously decided to use a guard to avoid introducing a breaking change.

However, I'm happy to remove calling getcwd(). As you indicate, arguably, the issue I ran into demonstrates the function isn't the proper approach to choose there because it's brittle.

Let me know if you'd be willing to introduce a breaking change.

@justinmk
Copy link
Copy Markdown
Member

justinmk commented Mar 14, 2026

From my understanding, neovim buffer names are always plain paths (e.g.
/home/user/file.ts) for real files, never file:// URIs

That may change in the future though.

I guess you could future-proof your current PR by only skipping if is_uri and root() == nil

@igorlfs
Copy link
Copy Markdown
Contributor

igorlfs commented Mar 16, 2026

Using vim.uv.fs_stat might be preferable here? See #3899

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.

3 participants