{"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":185,"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":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/ruvnet/ruflo/blob/2602b642d92234c710ffbe96bfb33007d481ceab/v3/@claude-flow/cli/src/mcp-server.ts#L167-L203","documentation":"MCPServer.start() first calls getStatus(); if it reports a running server under a different PID than the current process, it throws 'MCP Server already running (PID: N)'. The PID self-check exists because in stdio mode getStatus() reports the current process as running before it actually starts — any other PID means a genuine second instance, and the server refuses to double-bind the transport/state.","triggerScenarios":"A previous `mcp start`/daemon still alive in another terminal or tmux pane; a background server from an earlier session that survived shell exit; an orphaned process after a crash whose status record still resolves to a live PID; CI jobs overlapping because the previous job's server was never stopped.","commonSituations":"Dev loop where the daemon was started once and forgotten; machine/CI runner reuse where stray processes persist between jobs; running both an interactive MCP session and a background daemon against the same state directory.","solutions":["Stop the existing instance first: `kill <PID from message>` or call MCPServer.stop() (mcp-server.ts:222), then start again","If the reported PID no longer exists (`ps -p <PID>` empty), the status record is stale — remove the status/PID file the getStatus() probe reads, or run `npx @claude-flow/cli@latest doctor` to clean up","Wrap start in a guard script: check getStatus().running before calling start()"],"exampleFix":"# before — second start collides\nnpx @claude-flow/cli@latest mcp start  # Error: MCP Server already running (PID: 4242)\n\n# after — stop, then start (or reuse the running one)\nkill 4242   # or: npx @claude-flow/cli@latest daemon stop\nnpx @claude-flow/cli@latest mcp start","handlingStrategy":"validation","validationCode":"const status = await server.getStatus();\nif (status.running && status.pid !== process.pid) {\n  console.log(`server already running (PID ${status.pid}) — stopping first`);\n  await server.stop(); // mcp-server.ts:222\n}\nawait server.start();","typeGuard":null,"tryCatchPattern":"try { await server.start(); }\ncatch (e) {\n  const m = /already running \\(PID: (\\d+)\\)/.exec((e as Error).message);\n  if (m) { await server.stop(); await server.start(); } // or: kill stale PID and retry once\n  else throw e;\n}","preventionTips":["Check getStatus() before start() in scripts and CI jobs","Always stop the server/daemon in cleanup handlers (finally blocks, trap EXIT) so no instance leaks","On 'already running' with a dead PID, treat the status record as stale and clean it before retrying"],"tags":["mcp","server","process-management","single-instance","daemon"],"backgroundTag":"service-already-running","analyzedSha":"2602b642d92234c710ffbe96bfb33007d481ceab","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}