{"record":{"id":"60e5269a15b59c83","repo":"can1357/oh-my-pi","slug":"lsp-mux-is-already-listening","errorCode":null,"errorMessage":"LSP mux is already listening","messagePattern":"LSP mux is already listening","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/lsp/mux/server.ts","lineNumber":202,"sourceCode":"\t#endpoint?: string;\n\t#idleTimer?: NodeJS.Timeout;\n\t#activityClock = Date.now();\n\t#shuttingDown = false;\n\t#shutdownPromise?: Promise<void>;\n\n\t/** Number of currently connected mux links, including unbound ping links. */\n\tget sessionCount(): number {\n\t\treturn this.#sessions.size;\n\t}\n\n\t/** Keys of currently live shared language-server children. */\n\tget serverKeys(): string[] {\n\t\treturn [...this.#servers].map(server => server.key);\n\t}\n\n\t/** Listen for Content-Length framed mux links at a Unix socket or named pipe. */\n\tasync listen(endpoint: string): Promise<void> {\n\t\tif (this.#netServer) throw new Error(\"LSP mux is already listening\");\n\t\tif (process.platform !== \"win32\") await this.#clearStaleSocket(endpoint);\n\t\tconst server = net.createServer(socket => this.#accept(socket));\n\t\tthis.#netServer = server;\n\t\tthis.#endpoint = endpoint;\n\t\tconst { promise, resolve, reject } = Promise.withResolvers<void>();\n\t\tconst onError = (error: Error) => reject(error);\n\t\tserver.once(\"error\", onError);\n\t\tserver.listen(endpoint, () => {\n\t\t\tserver.off(\"error\", onError);\n\t\t\tresolve();\n\t\t});\n\t\tawait promise;\n\t\tthis.#armMuxIdle();\n\t}\n\n\t/** Gracefully close all children, links, and the listening endpoint. */\n\tasync shutdown(): Promise<void> {\n\t\tthis.#shutdownPromise ??= this.#performShutdown();","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/lsp/mux/server.ts#L184-L220","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait mux.listen(sockPath);\n// after\nif (!mux.isListening) await mux.listen(sockPath);","handlingStrategy":"try-catch","validationCode":"// Track listening state at the callsite\nlet listening = false;\nasync function ensureListening(mux, endpoint) {\n  if (listening) return;\n  await mux.listen(endpoint);\n  listening = true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await server.listen(endpoint);\n} catch (err) {\n  if (err.message === 'LSP mux is already listening') {\n    // no-op: server already serving at #endpoint; reuse it\n    return;\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["lsp","mux","double-listen","ipc","state"],"backgroundTag":"server-already-listening","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}