ruvnet/ruflo · error

ruflo proxy supervisor failed to spawn — no PID returned

Error message

ruflo proxy supervisor failed to spawn — no PID returned

What it means

After spawning the detached supervisor (process.execPath + ['proxy', 'supervise']), Node returned a ChildProcess without a pid. The PID is required to write the pid file, so startBackground() aborts before recording anything. This is a rare spawn-level runtime failure — the supervisor never became observable, not a proxy-level crash.

Solutions

  1. Retry the start once — transient resource pressure is the most common cause
  2. Check process limits on the host (`ulimit -u`, container pids.max) and raise them or reduce load
  3. Inspect the service log file for an immediately-exiting supervisor; free resources and run `ruflo proxy start --service` again
Defensive patterns

Strategy: retry

Validate before calling

import { readFileSync } from 'node:fs';
// Host-level pre-check for hardened Linux environments
try {
  const max = readFileSync('/sys/fs/cgroup/pids.max', 'utf8').trim();
  if (max !== 'max' && Number(max) < 100) throw new Error('cgroup pids.max too low for the supervisor');
} catch (e) { if (/pids\.max/.test(String(e))) throw e; } // absent file/cgroup: skip

Type guard

const isSpawnNoPid = (e: unknown): e is Error =>
  e instanceof Error && /failed to spawn — no PID returned/.test(e.message);

Try / catch

for (let attempt = 1; attempt <= 2; attempt++) {
  try {
    return await startBackground();
  } catch (e) {
    if (attempt === 2 || !isSpawnNoPid(e)) throw e;
    await new Promise(r => setTimeout(r, 500)); // transient resource pressure
  }
}

Prevention

When it happens

Trigger: spawn() returning a ChildProcess whose pid is undefined — typically resource exhaustion (fork limits, RLIMIT_NPROC, cgroup pids.max) or a broken runtime where the fork result is unavailable to Node.

Common situations: Containers with tight process limits; heavily loaded CI machines; sandboxed environments restricting process creation.

Related errors


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

Appendix: source

Thrown at v3/@claude-flow/cli/src/proxy/lifecycle.ts:230

  const lockFd = acquireStartLock();
  try {
    // Re-check under the lock — the dedup above raced a concurrent starter.
    const rechecked = getProxyStatus();
    if (rechecked.running && rechecked.pid) throw new ProxyAlreadyRunningError(rechecked.pid);
    if (rechecked.stalePidFile) clearStalePidFile();

    const logFd = fs.openSync(proxyLogFilePath(), 'a');
    const cliEntry = process.argv[1];
    if (!cliEntry) throw new Error('cannot locate the ruflo CLI entrypoint for supervised service mode');
    const child = spawn(process.execPath, [cliEntry, 'proxy', 'supervise'], {
      stdio: ['ignore', logFd, logFd],
      detached: true,
      windowsHide: true,
    });
    fs.closeSync(logFd); // the child holds its own duplicated fd; safe to close ours

    if (!child.pid) throw new Error('ruflo proxy supervisor failed to spawn — no PID returned');
    writePidFile(child.pid);
    child.unref();
    return { pid: child.pid };
  } finally {
    releaseStartLock(lockFd);
  }
}

export interface StopResult {
  wasRunning: boolean;
  pid: number | null;
}

/** SIGTERM -> 1000ms -> SIGKILL if still alive, mirroring daemon.ts's killBackgroundDaemon. */
export async function stopProxy(): Promise<StopResult> {
  const status = getProxyStatus();
  if (!status.running || !status.pid) {
    if (status.stalePidFile) clearStalePidFile();

View on GitHub (pinned to fa13ee4ad6)