ruvnet/ruflo · error · Error

WebSocket transport already running

Error message

WebSocket transport already running

What it means

WebSocketTransport.start() creates an HTTP server for the WebSocket upgrade (answering 426 to non-WS requests) and binds host, port and path (default '/ws'); it is guarded by a running flag. A second start() on the same instance throws because the port/path are already bound and client bookkeeping is live. stop() releases the server and resets the flag.

Source

Thrown at v3/@claude-flow/shared/src/mcp/transport/websocket.ts:92

  // Statistics
  private messagesReceived = 0;
  private messagesSent = 0;
  private errors = 0;
  private totalConnections = 0;

  constructor(
    private readonly logger: ILogger,
    private readonly config: WebSocketTransportConfig
  ) {
    super();
  }

  /**
   * Start the transport
   */
  async start(): Promise<void> {
    if (this.running) {
      throw new Error('WebSocket transport already running');
    }

    this.logger.info('Starting WebSocket transport', {
      host: this.config.host,
      port: this.config.port,
      path: this.config.path || '/ws',
    });

    // Create HTTP server for WebSocket upgrade
    this.server = createServer((req, res) => {
      // Simple HTTP response for non-WebSocket requests
      res.writeHead(426, { 'Content-Type': 'text/plain' });
      res.end('Upgrade Required - WebSocket connection expected');
    });

    // Create WebSocket server
    this.wss = new WebSocketServer({
      server: this.server,

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Memoize: startPromise ??= transport.start()
  2. Restart properly: await transport.stop() then start()
  3. Otherwise create a new WebSocketTransport instance for the new lifecycle

Example fix

// before
await ws.start();
await reloadConfig();
await ws.start(); // throws: already running

// after
await ws.start();
await reloadConfig();
await ws.stop();
await ws.start();
Defensive patterns

Strategy: validation

Validate before calling

let startPromise: Promise<void> | null = null;
function ensureWsStarted() {
  return (startPromise ??= wsTransport.start());
}

Try / catch

try {
  await wsTransport.start();
} catch (e) {
  if (e instanceof Error && /already running/.test(e.message)) return;
  throw e;
}

Prevention

When it happens

Trigger: Calling start() twice; reconnect/reload logic reusing the transport without stop(); two modules sharing one WebSocketTransport instance both starting it; retry wrapper around start() firing after a slow first success.

Common situations: Config reload paths that rebuild wiring but keep the transport object; test harnesses sharing a module-level transport; supervisors that 'ensure started' by calling start() unconditionally.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/19c93370a3272fc0. Report an issue: GitHub.