{"record":{"id":"6eede3dd1b9d4d95","repo":"ruvnet/ruflo","slug":"websocket-transport-already-running","errorCode":null,"errorMessage":"WebSocket transport already running","messagePattern":"WebSocket transport already running","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/mcp/src/transport/websocket.ts","lineNumber":71,"sourceCode":"  private heartbeatTimer?: NodeJS.Timeout;\n  private running = false;\n  private connectionCounter = 0;\n\n  private messagesReceived = 0;\n  private messagesSent = 0;\n  private errors = 0;\n  private totalConnections = 0;\n\n  constructor(\n    private readonly logger: ILogger,\n    private readonly config: WebSocketTransportConfig\n  ) {\n    super();\n  }\n\n  async start(): Promise<void> {\n    if (this.running) {\n      throw new Error('WebSocket transport already running');\n    }\n\n    this.logger.info('Starting WebSocket transport', {\n      host: this.config.host,\n      port: this.config.port,\n      path: this.config.path || '/ws',\n    });\n\n    this.server = createServer((req, res) => {\n      res.writeHead(426, { 'Content-Type': 'text/plain' });\n      res.end('Upgrade Required - WebSocket connection expected');\n    });\n\n    this.wss = new WebSocketServer({\n      server: this.server,\n      path: this.config.path || '/ws',\n      maxPayload: this.config.maxMessageSize || 10 * 1024 * 1024,\n      perMessageDeflate: true,","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/mcp/src/transport/websocket.ts#L53-L89","documentation":"WebSocketTransport.start() creates the HTTP upgrade server and flips a private `running` flag; a second start() on the same instance throws 'WebSocket transport already running'. As with the other transports, only stop() resets the flag, so the error means the same object was started twice in its lifecycle.","triggerScenarios":"Calling start() from bootstrap and from a reconnect handler; hot-reload re-running init with a surviving singleton; startAll() followed by a manual transport.start() on the same instance.","commonSituations":"Reconnect logic that calls start() instead of checking state; tests that start in both setup and body; module-level singletons under watch mode.","solutions":["Await transport.stop() before start() when restarting the same instance","Create a fresh WebSocketTransport via createWebSocketTransport(logger, cfg) per lifecycle","Let TransportManager own the lifecycle (startAll/stopAll) instead of manual start calls"],"exampleFix":"// before\nawait wsTransport.start();\nawait wsTransport.start(); // throws\n\n// after\nasync function restart(t: ITransport) {\n  await t.stop();\n  await t.start();\n}","handlingStrategy":"validation","validationCode":"// WebSocketTransport.running is private; guard at the call site\nlet wsStarted = false;\nasync function ensureWsStarted(t: ITransport) {\n  if (wsStarted) return;\n  await t.start();\n  wsStarted = true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await wsTransport.start();\n} catch (e) {\n  if (e instanceof Error && e.message === 'WebSocket transport already running') {\n    // already up - no-op\n  } else {\n    throw e;\n  }\n}","preventionTips":["Reconnect logic should check state or stop() first, never blind-start","Prefer TransportManager.startAll()/stopAll() for lifecycle ownership","In watch mode, recreate transport instances instead of reusing module singletons"],"tags":["transport","websocket","lifecycle","server"],"backgroundTag":"already-running","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}