mise shims resolve from cwd, not buffer path
mise shims on PATH is the documented approach for Neovim IDE integration, but LSP servers started from the wrong cwd resolve the wrong tool version.
jdtls was crashing with exit code 1 when I opened a Java file via fuzzy finder: the mise shim resolved Java from nvim’s startup directory instead of the project’s mise.toml.
What I Learned
The mise IDE integration docs recommend prepending shims to PATH for Neovim:
-- init.lua
vim.env.PATH = vim.env.HOME .. "/.local/share/mise/shims:" .. vim.env.PATH
This benefits all tools (Node, Python, Java, etc.), and I added it. But shims resolve tool versions based on the process’s cwd, not the buffer’s file path. When I launch nvim from ~ and use <leader>fp to open a file in a project directory, nvim’s cwd stays at ~. The fuzzy finder opens the file without calling :cd.
LSP servers spawned by nvim inherit this cwd. The Mason jdtls wrapper calls java -version to validate the JDK. The shim at ~/.local/share/mise/shims/java resolves from ~, finds no mise.toml with a Java version, and falls through to the system /usr/bin/java, which is broken on this machine.
The fix is a hybrid. The shims PATH stays (it is the mise-documented baseline and benefits all tools). For LSP servers specifically, I resolve the tool path from the project root inside the root_dir(bufnr, on_dir) callback, before the server process spawns:
-- Resolve JAVA_HOME from the project root via mise.
-- root_dir resolves from the buffer's file path, not nvim's cwd.
local function set_java_home(dir)
if vim.fn.executable("mise") ~= 1 then
return
end
local result = vim.fn.systemlist("cd " .. vim.fn.shellescape(dir) .. " && mise where java 2>/dev/null")
if result and result[1] and result[1] ~= "" then
vim.env.JAVA_HOME = result[1]:gsub("[\n\r]", "")
end
end
I call set_java_home(root) before on_dir(root), so JAVA_HOME is set before the jdtls process spawns. The Mason wrapper finds JAVA_HOME and uses $JAVA_HOME/bin/java. This works regardless of where nvim was launched, because root_dir resolves from the buffer’s file path.
The mise docs mention mise where as a way to find tool paths programmatically. Combining shims PATH (for general tool resolution) with per-project mise where calls inside LSP callbacks (for cwd-independent resolution) covers both cases.
The full jdtls config, including the root_dir gate that scopes jdtls to the Maven backend, is in Scoping nvim-jdtls to a Maven subproject in a polyglot LazyVim setup.
References
- mise IDE Integration. The official Neovim recommendation: prepend shims to PATH. Also documents the limitation: “Once you have launched the IDE, it won’t reload the environment variables or the PATH.”
vim.lsp.enable(). Documents theroot_dir(bufnr, on_dir)callback signature used to setJAVA_HOMEbefore the server spawns.mise where. CLI command to resolve a tool’s install path for the current directory.
This post was written with AI assistance.