LSP client kills the server right after `exit`, so servers that save state on exit (jdtls) lose it

Status Open
Reported on v2.1.284
Maintainer reply None cached
Activity 1 comment · opened Oct 8, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

When Claude Code stops a plugin LSP server (e.g. at the end of a session), the LSP client's stop() sends shutdown, then the exit notification, and right after that its finally calls kill() (SIGTERM) on the server process. It never gives the process a chance to exit by itself. shutdownTimeout only bounds the shutdown/exit exchange, not the process exit.

Some servers do real work between exit and process exit. Eclipse JDT LS answers shutdown immediately and saves the workspace (JDT build state, the Gradle/Buildship model, indexes) in a job that its exit handler waits for. The SIGTERM lands in the middle of that save, so the next session finds no saved state: jdtls re-syncs Gradle and rebuilds every project from scratch. On our 80-project Gradle build that's about 20 minutes of CPU at every session start.

What Should Happen?

After sending exit, wait for the server process to exit (bounded by the remaining shutdownTimeout, or a few seconds when it's unset) and only then send SIGTERM. That's what vscode-languageclient does: it checks the process 2 s after exit and terminates it only if it's still alive (checkProcessDied in client/src/node/main.ts).

Error Messages/Logs

jdtls workspace log of a Claude Code session ends with:
!MESSAGE >> shutdown

After it: 1 of 80 .metadata/.plugins/org.eclipse.core.resources/.projects/*/org.eclipse.jdt.core/state.dat,
0 files in .metadata/.plugins/org.eclipse.buildship.core/project-preferences.

Same server and workspace, client waits for the process after `exit`:
>> shutdown   11:04:53.102
>> exit       11:04:53.104
Shutdown received... waking up main thread   11:04:54.715
-> 80 state.dat files, 84 Buildship preference files, next start is incremental.

Steps to Reproduce

  1. Add a plugin with an .lsp.json server running jdtls (jdtls -data <dir>) for .java, open a Gradle project, wait for the build to finish.
  2. End the session.
  3. Look at <dir>/.metadata/.log (ends at >> shutdown) and at the missing state.dat files; the next session rebuilds everything.

Claude Model

Not sure / Multiple models

Is this a regression?

No, this never worked

Claude Code Version

2.1.284 (same stop() code in 2.1.294)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other (tmux)

Additional Information

Code path in the bundle: createLSPClient → stop() → finally { Z(process, connection, { force: false }) } → process.kill().

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗