neoclide/coc.nvim · error · Error

Client is currently stopping. Can only restart a full stoppe

Error message

Client is currently stopping. Can only restart a full stopped client

What it means

LanguageClient._start() throws this when the client's state is ClientState.Stopping — a stop is in flight — and a start is requested. A client mid-shutdown cannot be started; it must reach a fully stopped state first. This guards against interleaving async start/stop transitions.

Source

Thrown at src/language-client/client.ts:992

  }

  public get started(): boolean {
    return this.$state != ClientState.Initial
  }

  public isRunning(): boolean {
    return this.$state === ClientState.Running
  }
  /**
   * @internal
   */

  public async _start(): Promise<void> {
    if (this._disposed === 'disposing' || this._disposed === 'disposed') {
      throw new Error(`Client got disposed and can't be restarted.`)
    }
    if (this.$state === ClientState.Stopping) {
      throw new Error(`Client is currently stopping. Can only restart a full stopped client`)
    }
    // We are already running or are in the process of getting up
    // to speed.
    if (this._onStart !== undefined) {
      return this._onStart
    }
    this._rootPath = this.resolveRootPath()

    const [promise, resolve, reject] = this.createOnStartPromise()
    this._onStart = promise

    this._diagnostics = undefined

    // When we start make all buffer handlers pending so that they
    // get added.
    for (const [method, handler] of this._notificationHandlers) {
      if (!this._pendingNotificationHandlers.has(method)) {
        this._pendingNotificationHandlers.set(method, handler)

View on GitHub (pinned to 50e974d969)

Solutions

  1. Await the pending stop() promise before calling start(); retry start after it resolves.
  2. Serialize lifecycle transitions through a queue/promise chain in extension code.
  3. Prefer client.restart() over manual stop+start — it handles state transitions.
  4. If state is wedged at Stopping, run :CocRestart or check for a hung server process (ps aux | grep <server>) blocking shutdown.

Example fix

// before
client.stop()
client.start() // throws: currently stopping

// after
await client.stop()
await client.start()
Defensive patterns

Strategy: retry

Try / catch

async function safeStart(client) {
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      await client.start()
      return
    } catch (e) {
      if (String(e).includes('currently stopping')) {
        await new Promise(r => setTimeout(r, 300))
        continue
      }
      throw e
    }
  }
}

Prevention

When it happens

Trigger: Calling client.start() while a previous client.stop() promise is still unresolved (server process still shutting down, dispose handlers running); rapid restart sequences (stop immediately followed by start without awaiting); :CocRestart racing with an in-flight start/stop.

Common situations: Autocommands (FocusGained/BufWritePost) triggering restarts while a stop is pending; watchers restarting servers on config change during shutdown; tests or scripts calling start/stop back-to-back without awaiting.

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/d66c974a1c2033a8. Report an issue: GitHub.