{"record":{"id":"c5f421aceac090c4","repo":"ruvnet/ruflo","slug":"mcp-server-already-running-pid-status-pid","errorCode":null,"errorMessage":"MCP Server already running (PID: ${status.pid})","messagePattern":"MCP Server already running \\(PID: (.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-server.ts","lineNumber":179,"sourceCode":"    // spread last below and therefore takes precedence over this env fallback.\n    const environmentTools = parseMcpToolSelection(process.env.CLAUDE_FLOW_MCP_TOOLS);\n    this.options = {\n      ...DEFAULT_OPTIONS,\n      ...(environmentTools === 'all' ? {} : { tools: environmentTools }),\n      ...options,\n    };\n  }\n\n  /**\n   * Start the MCP server\n   */\n  async start(): Promise<MCPServerStatus> {\n    // Check if already running (skip if status reports our own PID —\n    // getStatus() returns running=true for the current process in stdio mode\n    // even before the server is actually started)\n    const status = await this.getStatus();\n    if (status.running && status.pid !== process.pid) {\n      throw new Error(`MCP Server already running (PID: ${status.pid})`);\n    }\n\n    const startTime = performance.now();\n    this.startTime = new Date();\n\n    this.emit('starting', { options: this.options });\n\n    try {\n      if (this.options.transport === 'stdio') {\n        // For stdio transport, spawn the server process\n        await this.startStdioServer();\n      } else {\n        // For HTTP/WebSocket, start in-process server\n        await this.startHttpServer();\n      }\n\n      const duration = performance.now() - startTime;\n","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/mcp-server.ts#L161-L197","documentation":"Thrown by MCPServerManager.start() when getStatus() reports a running server whose PID differs from the current process. The guard exists so a second `mcp start` does not try to bind a port / spawn a duplicate stdio child while a prior server holds the PID file. The current-process PID is excluded because in stdio mode getStatus() can transiently report running=true for the very process about to start.","triggerScenarios":"A previous `mcp start` (HTTP or stdio) crashed without removing its PID file, leaving a stale lock; a daemon is genuinely running on the configured port; calling start() twice in the same script without stop(); a prior server forked a child that survived its parent.","commonSituations":"Daemon was killed -9 and left the PID file; port 3000 (or CLAUDE_FLOW_MCP_PORT) is held by an older instance; a CI runner reused a workspace without cleanup; two concurrent MCP startup paths (hook + manual start) race.","solutions":["Check the reported PID is actually an MCP server: `ps -p <pid> -o command=` and `npx @claude-flow/cli mcp status`.","If it is a stale/zombie entry, stop it cleanly: `npx @claude-flow/cli mcp stop`, or `kill <pid>` then remove the PID file.","If you need a second instance, set a different CLAUDE_FLOW_MCP_PORT / PID file path for the new one.","If start() was called twice in-process, call stop() between them, or guard with getStatus() first."],"exampleFix":"// before — second start races the first\nawait manager.start();\nawait manager.start(); // throws\n// after — stop or reuse\nawait manager.start();\n// ... work ...\nawait manager.stop();\nawait manager.start();","handlingStrategy":"validation","validationCode":"const status = await manager.getStatus();\nif (status.running && status.pid !== process.pid) {\n  // decide: stop the existing, or use a different port\n  throw new Error(`refusing to start: another MCP server holds the lock (PID ${status.pid})`);\n}","typeGuard":"null","tryCatchPattern":"try { await manager.start(); }\ncatch (e) {\n  if (/MCP Server already running \\(PID:/.test(String(e?.message ?? ''))) {\n    await manager.stop(true); // or pick a new port\n    await manager.start();\n  } else throw e;\n}","preventionTips":["Always pair start() with stop() in a finally block.","Use distinct PID-file paths / ports per process when running multiple instances.","Clean up the PID file in a process-exit handler so a crash does not leave a stale lock."],"tags":["mcp","server","pidfile","concurrency","startup"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}