Nvim-tree consumes all my memory
Description
As the title says, nvim-tree is using all my memory in a very particular scenario. I have the following tests inside a C++ application:
#include <filesystem>
int main(int argc, char** argv) {
std::filesystem::path testFolder("testResources");
// TODO: Internal FS
if (std::filesystem::exists(testFolder)) {
std::error_code ec{};
std::filesystem::remove_all(testFolder, ec);
}
if (!std::filesystem::exists(testFolder)) {
std::filesystem::create_directories(testFolder);
}
return 0;
}
(the test above only removes and recreate a directory if you are not used to C++).
when I run it on a folder that has nvim-tree enabled AND filesystem_watchers also enabled, it keeps allocating memory forever. If I disable filesystem_watchers the bug does not trigger. Upon inspection, the nvim-tree.log has the following lines repeated over and over:
[2025-09-02 23:01:51] [watcher] event_cb 'E:\Dev\bin\testResources' 'E:\Dev\bin\testResources'
[2025-09-02 23:01:51] [watcher] node event scheduled refresh explorer:watch:E:\Dev\bin\testResources:14
Neovim version
NVIM v0.11.4
Build type: Release
LuaJIT 2.1.1741730670
Operating system and version
Windows 10
Windows variant
PowerShell
nvim-tree version
321bc61
Clean room replication
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
vim.cmd([[set runtimepath=$VIMRUNTIME]])
vim.cmd([[set packpath=/tmp/nvt-min/site]])
local package_root = "/tmp/nvt-min/site/pack"
local install_path = package_root .. "/packer/start/packer.nvim"
local function load_plugins()
require("packer").startup({
{
"wbthomason/packer.nvim",
"nvim-tree/nvim-tree.lua",
"nvim-tree/nvim-web-devicons",
-- ADD PLUGINS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
},
config = {
package_root = package_root,
compile_path = install_path .. "/plugin/packer_compiled.lua",
display = { non_interactive = true },
},
})
end
if vim.fn.isdirectory(install_path) == 0 then
print("Installing nvim-tree and dependencies.")
vim.fn.system({ "git", "clone", "--depth=1", "https://github.com/wbthomason/packer.nvim", install_path })
end
load_plugins()
require("packer").sync()
vim.cmd([[autocmd User PackerComplete ++once echo "Ready!" | lua setup()]])
vim.opt.termguicolors = true
vim.opt.cursorline = true
-- MODIFY NVIM-TREE SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
_G.setup = function()
require("nvim-tree").setup({})
end
-- UNCOMMENT this block for diagnostics issues, substituting pattern and cmd as appropriate.
-- Requires diagnostics.enable = true in setup.
--[[
vim.api.nvim_create_autocmd("FileType", {
pattern = "lua",
callback = function()
vim.lsp.start {
name = "my-luals",
cmd = { "lua-language-server" },
root_dir = vim.loop.cwd(),
}
end,
})
]]
Steps to reproduce
- Put >=1 text files on the directory, doesnt matter which size
- Build the C++ application above and also put it on the directory
- nvim -nu nvt-min.lua
- :NvimTreeOpen
- Open any file in the directory
- Run the application 2 or 3 times
- Watch it explode
Expected behavior
No response
Actual behavior
No response
Possibly related #3196 fix #3197
Tried the above fix, it does not solve the problem
I've attempted to replicate on a linux ext4 file system. It looks like we are not destroying / recreating the watcher. It appears timing related, with different OS/FS giving different results.
Could you please repeat the test on WSL or another OS?
Either way this will be fixed.
all: rep
rep: rep.cpp
clean:
rm -f rep rep.o
CXX = clang++
make rep
echo foo > foo
echo bar > bar
echo baz > baz
open tree open foo
run rep several times
no runaway processing
[2025-09-04 08:18:16] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198' nil
[2025-09-04 08:18:16] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:18:16] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:18:32] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:18:32] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:18:32] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:18:32] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/testResources' nil
[2025-09-04 08:18:32] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:18:32] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:18:38] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/testResources' 'testResources'
[2025-09-04 08:18:38] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/testResources:2
[2025-09-04 08:18:38] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/testResources' 'testResources'
[2025-09-04 08:18:38] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/testResources:2
[2025-09-04 08:18:38] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:18:38] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:18:38] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:18:38] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:18:38] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:18:38] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:18:54] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:18:54] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:18:54] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:18:54] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:18:54] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:19:06] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:19:06] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:19:06] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:19:06] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:19:06] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:19:11] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:19:11] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:19:11] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:19:11] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:19:11] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
Putting a sleep in there results in expected logs:
#include <filesystem>
#include <unistd.h>
int main(int argc, char** argv) {
std::filesystem::path testFolder("testResources");
// TODO: Internal FS
if (std::filesystem::exists(testFolder)) {
std::error_code ec{};
std::filesystem::remove_all(testFolder, ec);
}
sleep(1);
if (!std::filesystem::exists(testFolder)) {
std::filesystem::create_directories(testFolder);
}
return 0;
}
[2025-09-04 08:36:04] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198' nil
[2025-09-04 08:36:04] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:36:04] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:36:04] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/d' nil
[2025-09-04 08:36:04] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/d'
[2025-09-04 08:36:04] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/d'
[2025-09-04 08:36:11] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:36:11] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:36:11] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:36:11] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/testResources' nil
[2025-09-04 08:36:11] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:36:11] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:36:16] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/testResources' 'testResources'
[2025-09-04 08:36:16] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/testResources:3
[2025-09-04 08:36:16] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/testResources' 'testResources'
[2025-09-04 08:36:16] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/testResources:3
[2025-09-04 08:36:16] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:36:16] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:36:16] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:36:16] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:36:16] [watcher] Watcher:destroy '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:36:16] [watcher] Event:destroy '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:36:17] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'testResources'
[2025-09-04 08:36:17] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-04 08:36:17] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-04 08:36:17] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/testResources' nil
[2025-09-04 08:36:17] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/testResources'
[2025-09-04 08:36:17] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/testResources'
Possibility: use a unique identifier for the file/dir instead of the generated UID https://github.com/nvim-tree/nvim-tree.lua/blob/3fc8de198c15ec4e5395f57b70579b3959976960/lua/nvim-tree/explorer/watch.lua#L85
Event could return a unique identifier based on the fs_event userdata from vim.loop.new_fs_event()
The event registry would need to be keyed by the above rather than the filename.
Hey, thanks for helping out, I really appreciate it :) I tried to reproduce the bug on WSL and it didn't happen. I then tried to reproduce it on WSL again but using the same ntfs folder as the windows test (didn't happen too). I also tried on Windows again but using another SSD and it happened again. Repeated the test with exFAT and still happens. Its definitely something with Windows and not with the underlying filesystem i'd say
Hey, thanks for helping out, I really appreciate it :) I tried to reproduce the bug on WSL and it didn't happen. I then tried to reproduce it on WSL again but using the same ntfs folder as the windows test (didn't happen too). I also tried on Windows again but using another SSD and it happened again. Repeated the test with exFAT and still happens. Its definitely something with Windows and not with the underlying filesystem i'd say
Many thanks for digging. Windows is known to behave strangely.
I'll make a branch for a fix, however I'm going to need you to test as I don't have access to windows.
Sure! Count me in :)
nvim implemented a watcher using similar fs_event_t mechanism, in 0.9, seemingly just for lsp https://github.com/neovim/neovim/pull/22405
~We could migrate to that.~
It look to be internal only, using recursive watchers, using many fs_stat calls.
This is quite complex:
- There is no mechanism in libuv to check the validity of a
uv_fs_event_t;is_activeetc. - An
fs_statat the time of the event should indicate the validity of the file, which could be torn down immediately. This would be very expensive and defeat the purpose of the efficient watcher. We don't want to do anything until after debouncing. - We could check at the time of the debounced callback. This would be too late, however we could check some sort of reference like
inohowever that would likely be very OS dependent uv_handle_tfilenois not useful, usually nil- event data in the callback is not useful - it's change and rename only, whatever that means
Yes, this is a problem that can be demonstrated on linux - stale directory watcher - however it's not been a problem, even with generated resources like node modules.
Perhaps we should just resolve the issue for windows powershell; a stat for each event may have to be the tradeoff.
@razor85 I'd be really grateful if you could collect some information for me. I'm interested in what windows supplies as the fileno and ino.
Please add the following logs to watcher.lua ~line 77
local event_cb = vim.schedule_wrap(function(err, filename, event)
local stat = vim.loop.fs_stat(self.path)
log.line("watcher", "event_cb '%s' fileno = %s event = %s fs_stat = %s",
self.path,
self.fs_event:fileno(),
vim.inspect(event, { newline = "" }),
stat and vim.inspect(stat.ino, { newline = "" })
)
A simple test of: Open nvim-tree
mkdir foo
touch foo/bar
rm foo/bar
rmdir foo
mkdir foo
touch foo/bar
I want to see if the ino changes when recreating the foo directory. For reference, on linux:
[2025-09-08 14:14:14] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728
[2025-09-08 14:14:14] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo'
[2025-09-08 14:14:14] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-08 14:14:14] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-08 14:14:14] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/foo' nil
[2025-09-08 14:14:14] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:14] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113148
[2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar'
[2025-09-08 14:14:20] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3
[2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { change = true} fs_stat = 4113148
[2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar'
[2025-09-08 14:14:20] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3
[2025-09-08 14:14:20] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:33] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113148
[2025-09-08 14:14:33] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar'
[2025-09-08 14:14:33] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3
[2025-09-08 14:14:33] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = nil
[2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'foo'
[2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3
[2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = nil
[2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'foo'
[2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3
[2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728
[2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo'
[2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-08 14:14:41] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:41] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-08 14:14:41] [watcher] Watcher:destroy '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:41] [watcher] Event:destroy '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:45] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728
[2025-09-08 14:14:45] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo'
[2025-09-08 14:14:45] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1
[2025-09-08 14:14:45] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198'
[2025-09-08 14:14:45] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/foo' nil
[2025-09-08 14:14:45] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:45] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/foo'
[2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113154
[2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar'
[2025-09-08 14:14:50] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:4
[2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { change = true} fs_stat = 4113154
[2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar'
[2025-09-08 14:14:50] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:4
[2025-09-08 14:14:50] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo'
Alternative / workaround:
This case seems very similar to node_modules and yarn, which creates / deletes / recreates a lot. It doesn't fail, however it's a massive resource hog, with many reported issues. We added node_modules to the default watcher ignore config https://github.com/nvim-tree/nvim-tree.lua/pull/2940
Would it be appropriate for you to do the same?
@razor85 I'd be really grateful if you could collect some information for me. I'm interested in what windows supplies as the fileno and ino.
Please add the following logs to
watcher.lua~line 77local event_cb = vim.schedule_wrap(function(err, filename, event) local stat = vim.loop.fs_stat(self.path) log.line("watcher", "event_cb '%s' fileno = %s event = %s fs_stat = %s", self.path, self.fs_event:fileno(), vim.inspect(event, { newline = "" }), stat and vim.inspect(stat.ino, { newline = "" }) )
A simple test of: Open nvim-tree
mkdir foo touch foo/bar rm foo/bar rmdir foo mkdir foo touch foo/bar
I want to see if the ino changes when recreating the
foodirectory. For reference, on linux:[2025-09-08 14:14:14] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728 [2025-09-08 14:14:14] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo' [2025-09-08 14:14:14] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1 [2025-09-08 14:14:14] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198' [2025-09-08 14:14:14] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/foo' nil [2025-09-08 14:14:14] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:14] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113148 [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:20] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { change = true} fs_stat = 4113148 [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:20] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:20] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:33] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113148 [2025-09-08 14:14:33] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:33] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:33] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = nil [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'foo' [2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = nil [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'foo' [2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728 [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo' [2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1 [2025-09-08 14:14:41] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:41] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198' [2025-09-08 14:14:41] [watcher] Watcher:destroy '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:41] [watcher] Event:destroy '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:45] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728 [2025-09-08 14:14:45] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo' [2025-09-08 14:14:45] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1 [2025-09-08 14:14:45] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198' [2025-09-08 14:14:45] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/foo' nil [2025-09-08 14:14:45] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:45] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113154 [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:50] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:4 [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { change = true} fs_stat = 4113154 [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:50] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:4 [2025-09-08 14:14:50] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo'
@razor85 I'd be really grateful if you could collect some information for me. I'm interested in what windows supplies as the fileno and ino.
Please add the following logs to
watcher.lua~line 77local event_cb = vim.schedule_wrap(function(err, filename, event) local stat = vim.loop.fs_stat(self.path) log.line("watcher", "event_cb '%s' fileno = %s event = %s fs_stat = %s", self.path, self.fs_event:fileno(), vim.inspect(event, { newline = "" }), stat and vim.inspect(stat.ino, { newline = "" }) )
A simple test of: Open nvim-tree
mkdir foo touch foo/bar rm foo/bar rmdir foo mkdir foo touch foo/bar
I want to see if the ino changes when recreating the
foodirectory. For reference, on linux:[2025-09-08 14:14:14] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728 [2025-09-08 14:14:14] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo' [2025-09-08 14:14:14] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1 [2025-09-08 14:14:14] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198' [2025-09-08 14:14:14] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/foo' nil [2025-09-08 14:14:14] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:14] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113148 [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:20] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { change = true} fs_stat = 4113148 [2025-09-08 14:14:20] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:20] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:20] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:33] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113148 [2025-09-08 14:14:33] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:33] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:33] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = nil [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'foo' [2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = nil [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'foo' [2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:3 [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728 [2025-09-08 14:14:41] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo' [2025-09-08 14:14:41] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1 [2025-09-08 14:14:41] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:41] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198' [2025-09-08 14:14:41] [watcher] Watcher:destroy '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:41] [watcher] Event:destroy '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:45] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' fileno = nil event = { rename = true} fs_stat = 4097728 [2025-09-08 14:14:45] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198' 'foo' [2025-09-08 14:14:45] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198:1 [2025-09-08 14:14:45] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198' [2025-09-08 14:14:45] [watcher] Watcher:create '/home/alex/src/nvim-tree/r/3198/foo' nil [2025-09-08 14:14:45] [watcher] Event:create '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:45] [watcher] Event:start '/home/alex/src/nvim-tree/r/3198/foo' [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { rename = true} fs_stat = 4113154 [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:50] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:4 [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' fileno = nil event = { change = true} fs_stat = 4113154 [2025-09-08 14:14:50] [watcher] event_cb '/home/alex/src/nvim-tree/r/3198/foo' 'bar' [2025-09-08 14:14:50] [watcher] node event scheduled refresh explorer:watch:/home/alex/src/nvim-tree/r/3198/foo:4 [2025-09-08 14:14:50] [watcher] node event executing refresh '/home/alex/src/nvim-tree/r/3198/foo'
Sure. I ran the commands up until the last rmdir (Remove-Item on powershell) when nvim gave me an error:
Error executing vim.schedule lua callback: ...ack\packer\start\nvim-tree.lua/lua/nvim-tree/watcher.lua:81: attempt to index field 'fs_event' (a nil value) stack traceback: ...ack\packer\start\nvim-tree.lua/lua/nvim-tree/watcher.lua:81: in function 'fn' vim/_editor.lua:366: in function <vim/_editor.lua:365>
The rest of the logs are on https://gist.github.com/razor85/121211ce99814d611af7ff2f5bc8ce76 (gh didn't allow me to embed the big output here)
Alternative / workaround:
This case seems very similar to
node_modulesand yarn, which creates / deletes / recreates a lot. It doesn't fail, however it's a massive resource hog, with many reported issues. We addednode_modulesto the default watcher ignore config #2940Would it be appropriate for you to do the same?
This workaround won't work for me because I have multiple repositories / nested folders with tests and that will make nvim-tree skip a big chunk of them due to a problematic occurrence. Since I have a shared codebase that might also happens on other projects so if the bug is here to stay I'll just disable the file watcher and refresh manually :)
Sure. I ran the commands up until the last rmdir (Remove-Item on powershell) when nvim gave me an error:
That's great - thank you.
It shows us that there is a unique ino, the removed directory will not stat (reappears at one point) and the fs_event watcher is eventually destroyed, after a lot of thrashing.
The solution of an fs_stat for each event is thus viable.
This workaround won't work for me because I have multiple repositories / nested folders with tests and that will make nvim-tree skip a big chunk of them due to a problematic occurrence. Since I have a shared codebase that might also happens on other projects so if the bug is here to stay I'll just disable the file watcher and refresh manually :)
Understood, just thought I'd share.