ruvnet/ruflo · error · Error
Server failed to start within timeout
Error message
Server failed to start within timeout
What it means
waitForReady() polls checkHealth() every 100 ms until the timeout (default 10 s) elapses; if the server never reports healthy it throws 'Server failed to start within timeout'. For stdio transport the method returns immediately, so this error is specific to network transports (http) where readiness depends on the spawned process actually binding and answering health checks.
Solutions
- Check server logs and confirm the port is free and the configured host is bindable (lsof -i :PORT) — a crashed child never becomes healthy
- Raise the readiness timeout passed to waitForReady()/start options on slow machines, or retry start with backoff
- Reduce startup work (defer model/memory initialization) or pre-warm caches so health passes sooner
- If it never turns healthy even with generous timeouts, run `npx @claude-flow/cli@latest doctor` to check Node version and environment
Example fix
// before — default 10s readiness window on a cold CI runner
await server.start(); // throws 'Server failed to start within timeout'
// after — explicit longer timeout + one retry
await server.start({ readyTimeout: 60_000 });
// or: await retry(() => server.start(), { retries: 2, backoffMs: 5000 }); Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight for http transport: ensure the port is free before start
import { createServer } from 'node:net';
await new Promise<void>((res, rej) => {
const probe = createServer();
probe.once('error', rej).once('listening', () => probe.close(() => res()));
probe.listen({ port: options.port, host: options.host });
}); Try / catch
async function startWithRetry(server, { retries = 2, timeoutMs = 30_000 } = {}) {
for (let attempt = 0; ; attempt++) {
try { return await server.start(); }
catch (e) {
if ((e as Error).message !== 'Server failed to start within timeout' || attempt >= retries) throw e;
await server.stop().catch(() => {});
await new Promise(r => setTimeout(r, 2 ** attempt * 1000)); // backoff
}
}
} Prevention
- Raise the readiness timeout on slow CI/cold-start environments
- Free the port and check logs before retrying — a crashed child never becomes healthy
- Pre-warm model/memory caches so health checks pass inside the window
When it happens
Trigger: HTTP transport on a busy or already-bound port so the child crashes and health never passes; cold start with heavy initialization (memory/ONNX model loading) exceeding the 10 s default; host/port misconfiguration (binding 0.0.0.0 vs configured host, firewall dropping health probes); an underpowered CI runner where startup is systematically slow.
Common situations: CI pipelines on shared runners with slow disk; first run downloading models; port conflicts with a previous server (see 226) causing crash-restart loops; container CPU limits throttling startup past the timeout.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Failed to register MCP tools
- INTERNAL_ERROR
- Meta-Proxy v did not become the effective daemon.
- Another Ruflo/MetaHarness installer still owns the…
- at least one candidate is required
AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-08-18).
Data as JSON: /api/errors/cf441ad7ecf382ca.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-server.ts:809
* 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 2602b642d9)