neovim icon indicating copy to clipboard operation
neovim copied to clipboard

LSP: nil reference in `compute_start_range`

Open justinmk opened this issue 1 year ago • 11 comments

Problem

Error executing lua callback: /usr/local/share/nvim/runtime/lua/vim/lsp/sync.lua:142: attempt to get length of local 'prev_line' (a nil value)
stack traceback:
        /usr/local/share/nvim/runtime/lua/vim/lsp/sync.lua:142: in function 'compute_start_range'
        /usr/local/share/nvim/runtime/lua/vim/lsp/sync.lua:390: in function 'compute_diff'
        ...local/share/nvim/runtime/lua/vim/lsp/_changetracking.lua:106: in function 'incremental_changes'
        ...local/share/nvim/runtime/lua/vim/lsp/_changetracking.lua:311: in function 'send_changes_for_group'
        ...local/share/nvim/runtime/lua/vim/lsp/_changetracking.lua:348: in function 'send_changes'
        /usr/local/share/nvim/runtime/lua/vim/lsp.lua:869: in function </usr/local/share/nvim/runtime/lua/vim/lsp.lua:863>

Related: https://github.com/neovim/neovim/issues/33142

Steps to reproduce using "nvim -u minimal_init.lua"

Don't have exact steps. Try using lsp-driven autocompletion in a buftype=prompt buffer.

Expected behavior

lsp module should never fail in this way. It should check that buffers/windows are valid before trying to operate on them.

Nvim version (nvim -v)

0.12

Language server name/version

any

Operating system/version

macos

Log file

No response

justinmk avatar Mar 31 '25 13:03 justinmk

Hi, I am getting a similar error message when using :AvanteEdit on a block of code that begins with file first line (I think). It does not happen with neovim 0.10.4, is reproducible every time with 0.11.0. Thank you.

Error executing vim.schedule lua callback: ...cal/share/nvim/lazy/avante.nvim/lua/avante/selection.lua:164: Vim:Error executing lua callback
: ...unwrapped-0.11.0/share/nvim/runtime/lua/vim/lsp/sync.lua:195: attempt to get length of local 'prev_line' (a nil value)
stack traceback:
        ...unwrapped-0.11.0/share/nvim/runtime/lua/vim/lsp/sync.lua:195: in function 'compute_end_range'
        ...unwrapped-0.11.0/share/nvim/runtime/lua/vim/lsp/sync.lua:401: in function 'compute_diff'
        ....11.0/share/nvim/runtime/lua/vim/lsp/_changetracking.lua:106: in function 'incremental_changes'
        ....11.0/share/nvim/runtime/lua/vim/lsp/_changetracking.lua:311: in function 'send_changes_for_group'
        ....11.0/share/nvim/runtime/lua/vim/lsp/_changetracking.lua:348: in function 'send_changes'
        ...ovim-unwrapped-0.11.0/share/nvim/runtime/lua/vim/lsp.lua:869: in function <...ovim-unwrapped-0.11.0/share/nvim/runtime/lua/vim/ls
p.lua:863>
        [C]: in function 'nvim_buf_set_lines'
        ...cal/share/nvim/lazy/avante.nvim/lua/avante/selection.lua:164: in function ''
        vim/_editor.lua: in function <vim/_editor.lua:0>
stack traceback:
        [C]: in function 'nvim_buf_set_lines'
        ...cal/share/nvim/lazy/avante.nvim/lua/avante/selection.lua:164: in function ''
        vim/_editor.lua: in function <vim/_editor.lua:0>

Kamilcuk avatar Apr 02 '25 10:04 Kamilcuk

@Kamilcuk are you using vim.lsp.config / vim.lsp.enable ? I'm wondering if this correlates with that.

justinmk avatar Apr 02 '25 12:04 justinmk

In Neovim 0.11, this error occurs whenever a swap file is present for the active buffer. Deleting the swap file fixes the error.

jrasanen avatar Apr 17 '25 18:04 jrasanen

are you using vim.lsp.config / vim.lsp.enable ?

yes

Kamilcuk avatar Apr 17 '25 22:04 Kamilcuk

@justinmk Hey, I've observed the issue described by @Kamilcuk as well (currently on nightly), calling:

:lua vim.api.nvim_buf_set_lines(0, 0, 0, false, {})

on empty file with single line explodes on the assert

https://github.com/neovim/neovim/blob/a0d94ac4692a046e88850d512112754c4b8d7013/runtime/lua/vim/lsp/sync.lua#L192-L195

Robitx avatar Aug 08 '25 16:08 Robitx

Unless you have steps to repro with nvim --clean, don't bother with "me too" comments.

justinmk avatar Aug 08 '25 21:08 justinmk

Literally the comment above this one says to stop posting "me too" comments. Read the room, people.

justinmk avatar Aug 19 '25 14:08 justinmk

This type of issues will come more and more as we use more agentic ai , the situation of having an external process modifying buffers that you have opened in nvim.

bechampion avatar Nov 07 '25 11:11 bechampion

I've worked with claude for a bit to come up with a "fix" obvs this needs testing and what not

diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index 36a94d5fba..9783b7856f 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -133,8 +133,14 @@ local function compute_start_range(
     return { line_idx = firstline, byte_idx = 1, char_idx = 1 }
   end
 
-  local prev_line = assert(prev_lines[firstline])
-  local curr_line = assert(curr_lines[firstline])
+  -- Bounds check: ensure prev_lines and curr_lines have the required indices
+  if not prev_lines or not prev_lines[firstline] or not curr_lines or not curr_lines[firstline] then
+    -- If we can't get the lines, return a safe default
+    return { line_idx = firstline, byte_idx = 1, char_idx = 1 }
+  end
+
+  local prev_line = prev_lines[firstline]
+  local curr_line = curr_lines[firstline]
 
   -- Iterate across previous and current line containing first change
   -- to find the first different byte.
@@ -192,12 +198,17 @@ local function compute_end_range(
   -- A special case for the following `firstline == new_lastline` case where lines are deleted.
   -- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol.
   if #curr_lines == 1 and curr_lines[1] == '' then
-    local prev_line = assert(prev_lines[lastline - 1])
-    return {
-      line_idx = lastline - 1,
-      byte_idx = #prev_line + 1,
-      char_idx = str_utfindex(prev_line, position_encoding) + 1,
-    }, { line_idx = 1, byte_idx = 1, char_idx = 1 }
+    if prev_lines and prev_lines[lastline - 1] then
+      local prev_line = prev_lines[lastline - 1]
+      return {
+        line_idx = lastline - 1,
+        byte_idx = #prev_line + 1,
+        char_idx = str_utfindex(prev_line, position_encoding) + 1,
+      }, { line_idx = 1, byte_idx = 1, char_idx = 1 }
+    else
+      -- Fallback if prev_line doesn't exist
+      return { line_idx = lastline - 1, byte_idx = 1, char_idx = 1 }, { line_idx = 1, byte_idx = 1, char_idx = 1 }
+    end
   end
   -- If firstline == new_lastline, the first change occurred on a line that was deleted.
   -- In this case, the last_byte...
@@ -222,8 +233,13 @@ local function compute_end_range(
   local prev_line_idx = lastline - 1
   local curr_line_idx = new_lastline - 1
 
-  local prev_line = assert(prev_lines[lastline - 1])
-  local curr_line = assert(curr_lines[new_lastline - 1])
+  -- Bounds check for last lines
+  if not prev_lines or not prev_lines[lastline - 1] or not curr_lines or not curr_lines[new_lastline - 1] then
+    return { line_idx = lastline - 1, byte_idx = 1, char_idx = 1 }, { line_idx = new_lastline - 1, byte_idx = 1, char_idx = 1 }
+  end
+
+  local prev_line = prev_lines[lastline - 1]
+  local curr_line = curr_lines[new_lastline - 1]
 
   local prev_line_length = #prev_line
   local curr_line_length = #curr_line

48 hours in , so far so good

bechampion avatar Nov 10 '25 15:11 bechampion

This type of issues will come more and more as we use more agentic ai

Yes, but regardless of AI, we should handle this better. Did you send a PR, ideally with a test? Thanks!

justinmk avatar Nov 14 '25 22:11 justinmk

Hi @bechampion,

Is it possible to create a PR for this?

Thanks a lot

fkhoda avatar Nov 23 '25 18:11 fkhoda