{"record":{"id":"96d53b8a90c9aeeb","repo":"ruvnet/ruflo","slug":"competing-meta-proxy-pid-current-pid-won-the-po","errorCode":null,"errorMessage":"Competing Meta-Proxy pid ${current.pid} won the port with version ${current.version}.","messagePattern":"Competing Meta-Proxy pid (.+?) won the port with version (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/proxy/activation.ts","lineNumber":144,"sourceCode":"  const log = fs.openSync(proxyLogFilePath(), 'a', 0o600);\n  try {\n    const child = spawn(binary, [], { detached: true, stdio: ['ignore', log, log], windowsHide: true });\n    if (!child.pid) throw new Error('Meta-Proxy did not return a process id.');\n    child.unref();\n    fs.writeFileSync(proxyPidFilePath(), `${child.pid}\\n`, { mode: 0o600 });\n    return child.pid;\n  } finally { fs.closeSync(log); }\n}\n\nasync function launchAndVerify(binary: string, version: string, wait: Wait): Promise<EffectiveProxy> {\n  const pid = launch(binary);\n  for (let attempt = 0; attempt < 100; attempt++) {\n    await wait(50);\n    const current = await probeEffectiveProxy();\n    if (current?.pid === pid && current.version === version && path.resolve(current.executable) === path.resolve(binary)) return current;\n    if (current && current.pid !== pid) {\n      try { process.kill(pid, 'SIGTERM'); } catch { /* already exited */ }\n      throw new Error(`Competing Meta-Proxy pid ${current.pid} won the port with version ${current.version}.`);\n    }\n  }\n  try { process.kill(pid, 'SIGTERM'); } catch { /* already exited */ }\n  throw new Error(`Meta-Proxy v${version} did not become the effective daemon.`);\n}\n\nexport async function installAndActivateProxy(version: string, log?: (line: string) => void): Promise<InstallResult & { pid: number }> {\n  const wait = waitNormally;\n  const binary = proxyBinaryPath();\n  const manifest = proxyInstallManifestPath();\n  const binaryBackup = `${binary}.rollback`;\n  const manifestBackup = `${manifest}.rollback`;\n  let release: (() => void) | null = null;\n  let prior: EffectiveProxy | null = null;\n  try {\n    release = await acquireProxyInstallLease(wait);\n    prior = await stopEffective(wait);\n    fs.rmSync(binaryBackup, { force: true });","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/ruvnet/ruflo/blob/29f048fc3b556f857cf2b126d2a84c19d2daa0d0/v3/@claude-flow/cli/src/proxy/activation.ts#L126-L162","documentation":"launchAndVerify() polls the effective proxy after launching the new binary, expecting the probe to return the pid it just started with the expected version and executable. If the probe instead returns a different pid, another daemon won the port race; the library kills its own (portless) child and throws, reporting the competing pid and version.","triggerScenarios":"installAndActivateProxy() or effective() → launchAndVerify(): while waiting up to 5s for the freshly spawned daemon to bind, another meta-proxy process (started concurrently, or respawned by a supervisor from the still-installed old binary) grabbed the port first.","commonSituations":"Two terminals/CI jobs activating the proxy at once; systemd restarting the previous daemon just after it was stopped; an old detached meta-proxy that hadn't fully exited when the new one launched; auto-update tooling launching its own instance.","solutions":["Ensure only one activation runs at a time — the install lease helps, but don't start daemons manually in parallel","Stop any service manager unit that respawns meta-proxy, run the install, then re-enable it","Find and stop the competing daemon (pid shown in the message), then retry installAndActivateProxy","Re-run the activation after a few seconds; if it recurs, check the proxy log and port usage (lsof -i :11435) to find who keeps binding"],"exampleFix":"// before: respawn racing the upgrade\n[program:meta-proxy]\nautorestart=true\n// after\n# supervisorctl stop meta-proxy\n# npx ruflo proxy install --version 1.2.3\n# supervisorctl start meta-proxy","handlingStrategy":"retry","validationCode":"// Before activating, make sure no other activation/daemon is mid-start\nconst existing = await probeEffectiveProxy();\nif (existing) console.log(`Existing proxy: pid ${existing.pid} v${existing.version} (${existing.executable}) — stop it first`);","typeGuard":"null","tryCatchPattern":"try {\n  await installAndActivateProxy(version);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('won the port')) {\n    await new Promise(r => setTimeout(r, 10_000));\n    await installAndActivateProxy(version); // competing transient daemon usually exits\n  } else throw e;\n}","preventionTips":["Run only one activation at a time (the install lease doesn't cover manual daemon starts)","Stop any supervisor unit for meta-proxy during upgrades, restart it afterwards","Don't manually launch meta-proxy in parallel with CLI-driven activation","In CI, dedicate one job to proxy installation"],"tags":["concurrency","race-condition","port-conflict","proxy"],"backgroundTag":"competing-daemon-won-port","analyzedSha":"29f048fc3b556f857cf2b126d2a84c19d2daa0d0","analyzedAt":"2026-09-01T11:37:30.251Z","contentChangedAt":"2026-09-01T11:37:30.251Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}