{"record":{"id":"efdc87e9d8fffe89","repo":"ruvnet/ruflo","slug":"http-transport-already-running","errorCode":null,"errorMessage":"HTTP transport already running","messagePattern":"HTTP transport already running","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/mcp/src/transport/http.ts","lineNumber":75,"sourceCode":"  private messagesReceived = 0;\n  private messagesSent = 0;\n  private errors = 0;\n  private httpRequests = 0;\n  private wsMessages = 0;\n\n  constructor(\n    private readonly logger: ILogger,\n    private readonly config: HttpTransportConfig\n  ) {\n    super();\n    this.app = express();\n    this.setupMiddleware();\n    this.setupRoutes();\n  }\n\n  async start(): Promise<void> {\n    if (this.running) {\n      throw new Error('HTTP transport already running');\n    }\n\n    this.logger.info('Starting HTTP transport', {\n      host: this.config.host,\n      port: this.config.port,\n    });\n\n    this.server = createServer(this.app);\n\n    this.wss = new WebSocketServer({\n      server: this.server,\n      path: '/ws',\n    });\n\n    this.setupWebSocketHandlers();\n\n    await new Promise<void>((resolve, reject) => {\n      this.server!.listen(this.config.port, this.config.host, () => {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/mcp/src/transport/http.ts#L57-L93","documentation":"HttpTransport.start() is not idempotent: a private `running` flag flips to true when the express server begins listening, and any second start() on the same instance throws 'HTTP transport already running'. The flag is private and only stop() resets it, so this error always means the same transport object was started twice without an intervening stop().","triggerScenarios":"Calling server.start() (or httpTransport.start()) from two code paths, e.g. bootstrap plus a health-check/reconnect handler; hot-reload (nodemon, vitest watch) re-running initialization while the old module-level instance survives; a retry loop around start() firing again after a slow success.","commonSituations":"Module-singleton transports re-initialized on re-import; MCP server started in both a setup hook and the test body; 'ensure running' helpers that call start() unconditionally.","solutions":["Call await transport.stop() before start() when restarting the same instance","Create a fresh instance via createHttpTransport(logger, cfg) for each lifecycle instead of restarting the old one","Route lifecycle through TransportManager and use its startAll()/stopAll()/isRunning()","Audit for duplicate start() call paths (bootstrap + reconnect/retry) and remove one"],"exampleFix":"// before\nawait httpTransport.start(); // throws if already started\n\n// after\nasync function startOnce(t: ITransport) {\n  try {\n    await t.start();\n  } catch (e) {\n    if (!(e instanceof Error && e.message.includes('already running'))) throw e;\n    await t.stop();\n    await t.start();\n  }\n}","handlingStrategy":"validation","validationCode":"// HttpTransport.running is private; track lifecycle at the call site\nlet httpStarted = false;\nasync function ensureHttpStarted(t: ITransport) {\n  if (httpStarted) return;\n  await t.start();\n  httpStarted = true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await httpTransport.start();\n} catch (e) {\n  if (e instanceof Error && e.message === 'HTTP transport already running') {\n    // already up - treat as no-op\n  } else {\n    throw e;\n  }\n}","preventionTips":["Start each transport from exactly one code path","Prefer TransportManager.startAll()/stopAll() over manual start/stop","On restart, fully await stop() before start()","In watch-mode tests, build new transport instances per run instead of reusing singletons"],"tags":["transport","http","lifecycle","server"],"backgroundTag":"already-running","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}