🐛 Stop the LSP shutdown context-canceled log

The TS 7 Go server races Session.updateWatches against the exit
notification; handleExit returns io.EOF, the background context is
cancelled, and the bare error is flushed to stderr once the outgoing
queue closes (server.go / logger.go). Send shutdown and close stdin
instead: EOF makes the server exit cleanly (code 0, no output), with the
kill kept as a fallback. testing.md records the cause.
This commit is contained in:
tmu committed 2026-09-18 07:31:04 +00:00
1 parent 13497d1df8
commit 8b73143740
2 files changed
+24 -16

No files matched your search

+7 -4
View File
@@ -165,10 +165,13 @@ transport and protocol plumbing are not hand-rolled (see
any order; the file is an integration test (~1.6 s) that needs `node_modules`. any order; the file is an integration test (~1.6 s) that needs `node_modules`.
`didOpen` is handled in order before the completion request, so no settle `didOpen` is handled in order before the completion request, so no settle
delay is needed. delay is needed.
- The Go server logs `context canceled` on stderr once per server as it shuts - The TS 7 Go server has a shutdown race: `handleExit` returns `io.EOF`, which
down (nine lines per `test:unit` run). It is benign and predates the protocol cancels the background context while `Session.updateWatches` is still running
client — the hand-rolled client produced the same line — so it is left as is (`internal/project/session.go`), so the bare `context canceled` error is
rather than filtered out of the server's stderr. flushed straight to stderr once the outgoing queue is closed, and the server
exits 1 even though LSP expects 0 after `shutdown`. The helper therefore sends
`shutdown` and then closes stdin instead of sending `exit`; EOF makes the
server exit cleanly (code 0, no output), and the kill is kept as a fallback.
## Known issues ## Known issues
+17 -12
View File
@@ -11,7 +11,6 @@ import {
ConfigurationRequest, ConfigurationRequest,
createMessageConnection, createMessageConnection,
DidOpenTextDocumentNotification, DidOpenTextDocumentNotification,
ExitNotification,
InitializedNotification, InitializedNotification,
InitializeRequest, InitializeRequest,
type MessageConnection, type MessageConnection,
@@ -140,18 +139,24 @@ export class LspSession {
); );
} }
/** `shutdown` + `exit`, then kill the server; safe after a failed probe. */ /** `shutdown` + close stdin, then kill the server; safe after a failed probe. */
public close(): Promise<void> { public close(): Promise<void> {
return this.#connection return (
.sendRequest(ShutdownRequest.type) this.#connection
.then(() => .sendRequest(ShutdownRequest.type)
this.#connection.sendNotification(ExitNotification.type), // The TS 7 Go server races a background task against the `exit`
) // notification and logs a bare `context canceled` to stderr as it
.then(() => delay(EXIT_DELAY_MS)) // tears down (see development/testing.md § Autocomplete). EOF on
.finally(() => { // stdin shuts it down cleanly instead: exit 0, no output.
this.#connection.dispose(); .then(() => {
this.#child.kill(); this.#child.stdin.end();
}); })
.then(() => delay(EXIT_DELAY_MS))
.finally(() => {
this.#connection.dispose();
this.#child.kill();
})
);
} }
#ensureInitialized(): Promise<void> { #ensureInitialized(): Promise<void> {