ruvnet/ruflo · error · Error
Server failed to start within timeout
Error message
Server failed to start within timeout
What it means
Thrown by MCPServerManager.waitForReady() after polling checkHealth() every 100ms for the configured timeout (default 10000ms) without ever seeing healthy=true. This is the HTTP/WebSocket path only — stdio returns immediately. It means the in-process HTTP server started but never answered the health endpoint (or checkHealth() keeps failing its TCP/HTTP probe).
Source
Thrown at v3/@claude-flow/cli/src/mcp-server.ts:751
* Wait for server to be ready
*/
private async waitForReady(timeout = 10000): Promise<void> {
// For stdio transport, we're ready immediately (in-process)
if (this.options.transport === 'stdio') {
return;
}
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const health = await this.checkHealth();
if (health.healthy) {
return;
}
await this.sleep(100);
}
throw new Error('Server failed to start within timeout');
}
/**
* Wait for process to exit
*/
private async waitForExit(timeout: number): Promise<void> {
if (!this.process) return;
return new Promise((resolve) => {
const timer = setTimeout(() => {
resolve();
}, timeout);
this.process!.once('exit', () => {
clearTimeout(timer);
resolve();
});
});View on GitHub (pinned to 6b01dc5a68)
Solutions
- Raise the timeout: pass a larger `timeout` to waitForReady, or set the relevant startup-timeout option in MCPServerOptions.
- Manually probe the health endpoint (`curl http://localhost:<port>/health`) to see if the server is up but the route differs from what checkHealth expects.
- Check that the port is actually free and bindable: `lsof -i:<port>` / `ss -ltn`.
- Profile cold-start — if sql.js/embeddings init dominates, warm those before start() or move to a persisted cache.
Example fix
// before — default 10s timeout too short in slow CI
await manager.start();
// after
await manager.start({ startupTimeoutMs: 30000 }); Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
const tryStart = async (timeoutMs: number) => {
try { return await manager.start({ startupTimeoutMs: timeoutMs }); }
catch (e) {
if (/Server failed to start within timeout/.test(String(e?.message ?? ''))) {
// backoff and retry once with a longer timeout
await new Promise(r => setTimeout(r, 2000));
return manager.start({ startupTimeoutMs: timeoutMs * 2 });
}
throw e;
}
}; Prevention
- Size startupTimeoutMs to your slowest cold-start (sql.js WASM, embeddings model load).
- Probe the port independently (`ss -ltn`) before calling start to rule out conflicts.
- Warm expensive deps (memory init, model load) before start() in latency-sensitive hosts.
When it happens
Trigger: The HTTP server bind() is blocked by a firewall or the port is in TIME_WAIT; the underlying createMCPServer threw asynchronously after start() returned; the health endpoint route is misconfigured; the timeout is too short for a slow-booting dependency (sql.js WASM load, embeddings model init).
Common situations: CI environment with slow disk or network causing cold-start > 10s; port conflict where the OS hands out the port slowly; a regression in the health route; the host is resource-starved (CPU throttled container).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- MCP Server already running (PID: ${status.pid})
- MCP server "${server.name}" is in cooldown (HTTP ${cd.status
- MCP server "${server.name}" returned HTTP ${httpStatus}: ${h
- HTTP transport failed: ${String(firstError instanceof Error
- No models available to build validation schema
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/cf441ad7ecf382ca.
Report an issue: GitHub.