{"record":{"id":"38a496aa0bb03ffd","repo":"ruvnet/ruflo","slug":"stdio-transport-already-running-38a496","errorCode":null,"errorMessage":"Stdio transport already running","messagePattern":"Stdio transport already running","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/shared/src/mcp/transport/stdio.ts","lineNumber":75,"sourceCode":"  private readonly outputStream: NodeJS.WritableStream;\n  private readonly maxMessageSize: number;\n\n  constructor(\n    private readonly logger: ILogger,\n    config: StdioTransportConfig = {}\n  ) {\n    super();\n    this.inputStream = config.inputStream || process.stdin;\n    this.outputStream = config.outputStream || process.stdout;\n    this.maxMessageSize = config.maxMessageSize || 10 * 1024 * 1024; // 10MB default\n  }\n\n  /**\n   * Start the transport\n   */\n  async start(): Promise<void> {\n    if (this.running) {\n      throw new Error('Stdio transport already running');\n    }\n\n    this.logger.info('Starting stdio transport');\n\n    // Create readline interface for efficient line processing\n    this.rl = readline.createInterface({\n      input: this.inputStream,\n      crlfDelay: Infinity,\n    });\n\n    // Handle incoming lines\n    this.rl.on('line', (line) => {\n      this.handleLine(line);\n    });\n\n    // Handle close\n    this.rl.on('close', () => {\n      this.handleClose();","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/shared/src/mcp/transport/stdio.ts#L57-L93","documentation":"StdioTransport.start() attaches a readline interface to the configured input stream (process.stdin by default, overridable via config.inputStream) and installs line handlers; it is guarded by a running flag. A second start() throws because the reader and handlers are already installed on the stream - restarting readline on a live stream would duplicate message processing.","triggerScenarios":"Calling start() twice on the same stdio transport; restart-on-reconnect code that forgets stop(); wiring the same transport instance into two bootstrap blocks; a supervisor loop that calls start() each time stdin data arrives.","commonSituations":"CLI tools assembling stdio transport in multiple init blocks; hot reload re-running bootstrap; tests sharing a module-scoped transport.","solutions":["Memoize startup: started ??= transport.start()","Restart via await transport.stop() (removes the readline interface) then start()","Or construct a fresh StdioTransport with explicit inputStream/outputStream for the new lifecycle"],"exampleFix":"// before\nawait stdio.start();\nonReconnect(async () => { await stdio.start(); }); // throws\n\n// after\nlet started = false;\nasync function ensureStarted() {\n  if (!started) { await stdio.start(); started = true; }\n}\nawait ensureStarted();","handlingStrategy":"validation","validationCode":"let started = false;\nasync function ensureStdioStarted() {\n  if (started) return;\n  await stdioTransport.start();\n  started = true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await stdioTransport.start();\n} catch (e) {\n  if (e instanceof Error && /already running/.test(e.message)) return;\n  throw e;\n}","preventionTips":["Track started state next to the transport instance; start exactly once","For restarts, stop() first (it removes the readline interface) or create a new StdioTransport","Provide explicit inputStream/outputStream in config when embedding, and own the lifecycle"],"tags":["mcp","transport","stdio","lifecycle","double-start"],"backgroundTag":"server-already-started","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}