opencode icon indicating copy to clipboard operation
opencode copied to clipboard

Disable LSP by default. Or run it at session end only, asking to fix errors in a subsequent message

Open remorses opened this issue 4 months ago • 10 comments

LSP is way too aggressive. The LLM shouldn't need to fix errors after a single tool call, because most edits are expected to create an invalid file, for example when removing a variable in different places. The LSP adds too much noise to the LLM

remorses avatar Sep 10 '25 18:09 remorses

This issue might be a duplicate of existing issues. Please check:

  • #2535: Very similar request to run formatters only on session end instead of after tool call, with the same reasoning that it confuses the LLM and reduces tool call success rates

Feel free to ignore if none of these address your specific case.

github-actions[bot] avatar Sep 10 '25 18:09 github-actions[bot]

+1, this pollutes the context and confuses the agent nonstop

divmgl avatar Sep 10 '25 22:09 divmgl

Mmm.. This is killer feature for me 😄 Other tools can use inexistent method or syntax. OpenCode always verifies after editing. If you expect errors and it's okay for you use case, you can instruct to ignore errors in the prompt. Its edge case in my opinion

NaikSoftware avatar Sep 12 '25 15:09 NaikSoftware

It's not about expecting errors in our code. It's about surfacing errors in the right moment. Surfacing errors on every message does not improve agent behavior.

divmgl avatar Sep 13 '25 00:09 divmgl

Here is how to force the model to call a tool without using a new user message @thdxr

await streamText({
  messages,                // prior user + assistant code
  tools: { typecheck }, // add LSP tool that typecheks among other messages
  toolChoice: { type: 'tool', toolName: 'typecheck' }, // <— force the tool call
});

This way the LLM will continue its assistant message without having to show the user an user message

Edit: I discovered doing this openai will not let the LLM call other tools so it's a bit useless

remorses avatar Sep 13 '25 11:09 remorses

It's not about expecting errors in our code. It's about surfacing errors in the right moment. Surfacing errors on every message does not improve agent behavior.

depends on the case. In most cases I faced, when LLM goes wrong way, the problem should be fixed immediately. On the end of the session can be too late. Many changes have been made to the code and at the end we say "the entire flow was broken, do something with this". Imagine that you use API for creating app and at the end it turns out that such API doesn't exist.

NaikSoftware avatar Sep 13 '25 11:09 NaikSoftware

I have found it adds a lot of noise/pollution to the context. The models are good enough now to mostly avoid creating compiler/language level errors when they make changes, and even when errors are made it quickly resolves them with a compile/type check after making changes.

If not disabled by default maybe add to the config a way to disable all LSPs rather than having to disable each on a language by language basis.

Here is an example just now when editing lua it thinks there's an error when there isn't (maybe due to lsp delay?) leading to the model then running a check only to realize there is no error ("Hmm, there's no error actually" followed by the Read tool use does not happen with lua LSP disabled):

  ┃                                                                                                                                         
  ┃  ← Edit nvim/.config/nvim/lua/plugins/sidekick.lua                                                                                      
  ┃                                                                                                                                         
  ┃   end,                                                                                                                                  
  ┃        desc = "Detach a CLI Session",                                                                                                   
  ┃    },                                                                                                                                   
  ┃    {                                                                                                                                    
  ┃   +    "az",                                                                                                                            
  ┃   +    function()                                                                                                                       
  ┃   +        require("sidekick.cli").hide()                                                                                               
  ┃   +    end,                                                                                                                             
  ┃   +    desc = "Hide CLI",                                                                                                               
  ┃   +},                                                                                                                                   
  ┃   +{                                                                                                                                    
  ┃        "<leader>aa",                                                                                                                    
  ┃        function()                                                                                                                       
  ┃            require("sidekick.cli").toggle({ name = "opencode", focus = true })                                                          
  ┃        end,                                                                                                                             
  ┃                                                                                                                                         
                                                                                                                                            
  ┃  Thinking: Let me read the file again to make sure I have the exact formatting right.                                                   
                                                                                                                                            
  ┃                                                                                                                                         
  ┃  # Check for Lua syntax errors                                                                                                          
  ┃                                                                                                                                         
  ┃  $ cd /Users/jbm/dotfiles/nvim/.config/nvim && nvim --headless -c "luafile lua/plugins/                                                 
  ┃  sidekick.lua" -c "quit" 2>&1                                                                                                           
  ┃                                                                                                                                         
                                                                                                                                            
  ┃  Thinking: Hmm, there's no error actually. Let me re-read the file to verify the edit was                                               
  ┃  applied.                                                                                                                               
                                                                                                                                            
     → Read nvim/.config/nvim/lua/plugins/sidekick.lua [offset=60, limit=20]                                                                
                                                                                                                                            
  ┃  Thinking: The edit was successfully applied! The new keymap az is now in the configuration                                             
  ┃  at lines 68-74, which will call require("sidekick.cli").hide() to hide the CLI.                                                        
                                                                                                                                            
     Done! Added az keymap to hide the CLI at line 68-74.                                                                                   

jbuckmccready avatar Nov 15 '25 00:11 jbuckmccready

I agree this is a real pain point. The current behavior forces users to choose between:

  1. "lsp": false - lose all LSP features (hover, go-to-definition, etc.)
  2. Keep LSP enabled - deal with noisy inline diagnostics after every edit

Suggestion: Add a config option like lsp.diagnostics.inline: boolean (default true to preserve current behavior). When set to false:

  • LSP servers still run, so hover/go-to-definition/references tools work
  • edit and write tools skip the LSP.touchFile() wait and diagnostics output

This way agents can run bun typecheck or similar commands explicitly when they're ready, rather than being interrupted by incomplete-code errors mid-edit.

ethan-huo avatar Jan 01 '26 15:01 ethan-huo

@ethan-huo what LSP features would u be losing if u turned them off, the only thing that would stop working is the lsp tool and that's experimental so u prolly dont have it anyway

rekram1-node avatar Jan 01 '26 18:01 rekram1-node

@ethan-huo what LSP features would u be losing if u turned them off, the only thing that would stop working is the lsp tool and that's experimental so u prolly dont have it anyway

I set OPENCODE_EXPERIMENTAL_LSP_TOOL=true

ethan-huo avatar Jan 02 '26 08:01 ethan-huo