Skip to content

[Detail Bug] Restart command calls stop() while client is Starting due to needsStop(), causing error #295

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_4c5b920c-9e04-4530-a4d2-953927859978/bugs/bug_46551983-d830-4599-aeef-326f361075a3

Summary

  • Context: The typos.restart command in the VSCode extension stops and restarts the language server client when configuration changes or when explicitly invoked by the user.
  • Bug: The restart command uses client.needsStop() to check if the client should be stopped before calling client.stop(), but needsStop() returns true for both Starting and Running states, while stop() only accepts the Running state.
  • Actual vs. expected: When the client is in the Starting state, needsStop() returns true but stop() throws an error "Client is not running and can't be stopped", causing the restart to fail. The expected behavior is that the restart should either wait for the client to finish starting or handle the Starting state gracefully.
  • Impact: If a user triggers multiple rapid configuration changes or manually executes the restart command while the language server is still starting, the extension will crash or show an error message, disrupting the user's workflow.

Code with bug

vscode.commands.registerCommand("typos.restart", async () => {
  // can't stop if the client has previously failed to start
  if (client && client.needsStop()) {  // <-- BUG 🔴 needsStop() true for Starting; stop() then throws
    await client.stop();
  }

Codebase inconsistency

  • From vscode-languageclient:
needsStop() {
    return this.$state === ClientState.Starting || this.$state === ClientState.Running;
}
// We can't stop a client that is not running (e.g. has no connection). Especially not
// on that us starting since it can't be correctly synchronized.
if (connection === undefined || this.$state !== ClientState.Running) {
    throw new Error(`Client is not running and can't be stopped. It's current state is: ${this.$state}`);
}
  • Conclusion: needsStop() returns true for Starting, but stop() throws unless state is Running. Using needsStop() to gate stop() causes an error when the client is starting.

Recommended fix

Only stop when the client is actually running:

vscode.commands.registerCommand("typos.restart", async () => {
  // Only stop if the client is running
  if (client && client.state === State.Running) {  // <-- FIX 🟢 restrict stop() to Running state
    await client.stop();
  }
  // ...recreate and start client
});

And ensure State is imported:

import { State } from "vscode-languageclient/node";

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions