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
- Guard callsites: only call listen when the server isn't already listening (check serverKeys/exposed state or a boolean flag)
- Reuse the already-listening server's endpoint instead of starting a new one
- 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
- Keep a single startup path responsible for calling listen
- Track listening state in a wrapper and make listen idempotent at the callsite
- If a fresh listener is genuinely needed, construct a new LspMuxServer instance instead
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
- lsp mux smoke failed: no ping response (${proc.peekStderr().
- Daemon broker client is closed
- LSP mux already listening on ${endpoint}
- LSP mux environment is incomplete
- directory stack is empty
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/60e5269a15b59c83.
Report an issue: GitHub.