can1357/oh-my-pi · error

LSP mux is already listening

Error message

LSP mux is already listening

What it means

LspMuxServer.listen sets up the Unix socket / named pipe listener for Content-Length framed mux links. Calling listen twice would attempt to bind a second net.Server while one is already active, so the method guards on #netServer and throws.

Source

Thrown at packages/coding-agent/src/lsp/mux/server.ts:202

	#endpoint?: string;
	#idleTimer?: NodeJS.Timeout;
	#activityClock = Date.now();
	#shuttingDown = false;
	#shutdownPromise?: Promise<void>;

	/** Number of currently connected mux links, including unbound ping links. */
	get sessionCount(): number {
		return this.#sessions.size;
	}

	/** Keys of currently live shared language-server children. */
	get serverKeys(): string[] {
		return [...this.#servers].map(server => server.key);
	}

	/** Listen for Content-Length framed mux links at a Unix socket or named pipe. */
	async listen(endpoint: string): Promise<void> {
		if (this.#netServer) throw new Error("LSP mux is already listening");
		if (process.platform !== "win32") await this.#clearStaleSocket(endpoint);
		const server = net.createServer(socket => this.#accept(socket));
		this.#netServer = server;
		this.#endpoint = endpoint;
		const { promise, resolve, reject } = Promise.withResolvers<void>();
		const onError = (error: Error) => reject(error);
		server.once("error", onError);
		server.listen(endpoint, () => {
			server.off("error", onError);
			resolve();
		});
		await promise;
		this.#armMuxIdle();
	}

	/** Gracefully close all children, links, and the listening endpoint. */
	async shutdown(): Promise<void> {
		this.#shutdownPromise ??= this.#performShutdown();

View on GitHub (pinned to 9690622007)

Solutions

  1. Guard callsites: only call listen when the server isn't already listening (check serverKeys/exposed state or a boolean flag)
  2. Reuse the already-listening server's endpoint instead of starting a new one
  3. Create a fresh LspMuxServer instance if a genuinely new listener is needed

Example fix

// before
await mux.listen(sockPath);
// after
if (!mux.isListening) await mux.listen(sockPath);
Defensive patterns

Strategy: try-catch

Validate before calling

// Track listening state at the callsite
let listening = false;
async function ensureListening(mux, endpoint) {
  if (listening) return;
  await mux.listen(endpoint);
  listening = true;
}

Try / catch

try {
  await server.listen(endpoint);
} catch (err) {
  if (err.message === 'LSP mux is already listening') {
    // no-op: server already serving at #endpoint; reuse it
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling server.listen(endpoint) on an LspMuxServer instance that already successfully listened (or whose listen is still in flight), e.g. daemon startup code executed twice, accidental re-invocation on reconnect, or a double-start race between two startup paths.

Common situations: Daemon supervisor retrying startup without checking state; hot-reload re-running init code; two startup hooks (CLI flag and config hook) both calling listen on the same server instance.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/60e5269a15b59c83. Report an issue: GitHub.