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
- Await the pending stop() promise before calling start(); retry start after it resolves.
- Serialize lifecycle transitions through a queue/promise chain in extension code.
- Prefer client.restart() over manual stop+start — it handles state transitions.
- 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
- Always await stop() before issuing start().
- Prefer client.restart() over manual stop+start sequences.
- Debounce restart triggers (config watchers, autocommands) to avoid overlapping transitions.
- Avoid stop/start from concurrent autocmds without a promise chain.
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
- Client is not running and can't be stopped. Its current stat
- Client got disposed and can't be restarted.
- Previous start failed. Can't restart server.
- Starting server failed
- ${uri} changed before apply edit
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/d66c974a1c2033a8.
Report an issue: GitHub.