ruvnet/ruflo · warning · ProxyAlreadyRunningError
meta-proxy is already running
Error message
meta-proxy is already running (pid ${pid}). Stop it first with: ruflo proxy stop What it means
startForeground() refuses to start a second meta-proxy: it reads the PID file and probes it with signal 0 (process.kill(pid, 0)); if a live process answers, ProxyAlreadyRunningError is thrown carrying the running PID. The check is skipped in supervised mode because there the supervisor's own PID was deliberately written by startBackground.
Solutions
- Stop the existing instance: `ruflo proxy stop`, then start again
- Run `ruflo proxy status` and inspect the live PID — you may just want to leave it running
- If the recorded PID belongs to an unrelated recycled process (pid reuse after reboot), follow the status output's stalePidFile hint to clear the stale pid file and retry
Example fix
# before ruflo proxy start # already running (pid 1234) # after ruflo proxy stop && ruflo proxy start
Defensive patterns
Strategy: try-catch
Validate before calling
import { getProxyStatus } from '@claude-flow/cli/.../proxy/lifecycle.js';
const status = getProxyStatus();
if (!status.running) {
await startForeground();
} else {
console.log(`already running, pid ${status.pid}`);
} Type guard
const isAlreadyRunning = (e: unknown): e is Error & { pid: number } =>
e instanceof Error && e.name === 'ProxyAlreadyRunningError'; Try / catch
try {
await startForeground();
} catch (e) {
if (isAlreadyRunning(e)) return; // idempotent start: already running is success
throw e;
} Prevention
- Check `ruflo proxy status` before start in scripts
- Treat ProxyAlreadyRunningError as success in idempotent automation
- Remember supervised mode intentionally skips this check — don't mix foreground and supervised modes blindly
When it happens
Trigger: `ruflo proxy start` (foreground, not --service) while a previous meta-proxy still runs; double-invoking start in two terminals; a `--service` instance started earlier still holding the recorded PID.
Common situations: A terminal left with the proxy in the foreground; forgetting an earlier `ruflo proxy start --service` is still alive; a second automation script starting the proxy unconditionally.
Related errors
- meta-proxy is not installed. Run: ruflo proxy install
- Agent not found
- Agent is not available (status: )
- Agent not initialized. Call initialize() first.
- AgentAdapter not initialized. Call initialize() first.
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/83c86b64092be16e.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/proxy/lifecycle.ts:177
fs.unlinkSync(proxyLockFilePath());
} catch {
/* ignore */
}
}
/**
* Foreground start (ADR-307 default) — blocks the caller until the process
* exits or is interrupted. `stdio: 'inherit'` passes the proxy's own output
* straight through to the terminal; signals (Ctrl+C) propagate naturally to
* the child, no manual forwarding needed.
*/
export async function startForeground(supervised = false): Promise<never> {
const bin = requireBinary();
const status = getProxyStatus();
// In service mode startBackground has already written this supervisor's
// PID. Treating it as a competing proxy makes the supervisor immediately
// exit before it can spawn meta-proxy.
if (!supervised && status.running && status.pid) throw new ProxyAlreadyRunningError(status.pid);
if (status.stalePidFile) clearStalePidFile();
const child = spawn(bin, [], { stdio: 'inherit', windowsHide: false });
if (!supervised && child.pid) writePidFile(child.pid);
const cleanup = () => clearStalePidFile();
process.on('exit', cleanup);
if (supervised) {
const forwardSignal = (signal: NodeJS.Signals) => {
if (!child.killed) child.kill(signal);
};
process.once('SIGTERM', () => forwardSignal('SIGTERM'));
process.once('SIGINT', () => forwardSignal('SIGINT'));
}
await new Promise<void>((resolve) => {
child.on('exit', () => {
cleanup();View on GitHub (pinned to fa13ee4ad6)