ruvnet/ruflo · error · Error
HTTP transport already running
Error message
HTTP transport already running
What it means
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().
Source
Thrown at v3/@claude-flow/mcp/src/transport/http.ts:75
private messagesReceived = 0;
private messagesSent = 0;
private errors = 0;
private httpRequests = 0;
private wsMessages = 0;
constructor(
private readonly logger: ILogger,
private readonly config: HttpTransportConfig
) {
super();
this.app = express();
this.setupMiddleware();
this.setupRoutes();
}
async start(): Promise<void> {
if (this.running) {
throw new Error('HTTP transport already running');
}
this.logger.info('Starting HTTP transport', {
host: this.config.host,
port: this.config.port,
});
this.server = createServer(this.app);
this.wss = new WebSocketServer({
server: this.server,
path: '/ws',
});
this.setupWebSocketHandlers();
await new Promise<void>((resolve, reject) => {
this.server!.listen(this.config.port, this.config.host, () => {View on GitHub (pinned to fa13ee4ad6)
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
Example fix
// before
await httpTransport.start(); // throws if already started
// after
async function startOnce(t: ITransport) {
try {
await t.start();
} catch (e) {
if (!(e instanceof Error && e.message.includes('already running'))) throw e;
await t.stop();
await t.start();
}
} Defensive patterns
Strategy: validation
Validate before calling
// HttpTransport.running is private; track lifecycle at the call site
let httpStarted = false;
async function ensureHttpStarted(t: ITransport) {
if (httpStarted) return;
await t.start();
httpStarted = true;
} Try / catch
try {
await httpTransport.start();
} catch (e) {
if (e instanceof Error && e.message === 'HTTP transport already running') {
// already up - treat as no-op
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- WebSocket transport already running
- HTTP transport already running
- HTTP transport requires host and port configuration
- Transport "${name}" already exists
- TransportManager already running
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/efdc87e9d8fffe89.
Report an issue: GitHub.