ruvnet/ruflo · critical

${failure} Rollback failed: ${rollbackError instanceof Error

Error message

${failure} Rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}

What it means

When installAndActivateProxy() fails after it had backed up the existing meta-proxy binary, it attempts a rollback (restore old binary/manifest, relaunch the previous daemon). If any rollback step itself throws — stopping the new daemon, restoring files, or relaunching the prior daemon — the original failure is rethrown wrapped with 'Rollback failed: <rollback error>' so both causes are visible.

Source

Thrown at v3/@claude-flow/cli/src/proxy/activation.ts:180

    fs.rmSync(binaryBackup, { force: true });
    fs.rmSync(manifestBackup, { force: true });
    if (fs.existsSync(binary)) fs.copyFileSync(binary, binaryBackup);
    if (fs.existsSync(manifest)) fs.copyFileSync(manifest, manifestBackup);
    const installed = await installProxy({ version, log });
    const effective = await launchAndVerify(installed.binaryPath, installed.version, wait);
    return { ...installed, pid: effective.pid };
  } catch (error) {
    const failure = error instanceof Error ? error.message : String(error);
    if (fs.existsSync(binaryBackup)) {
      try {
        await stopEffective(wait);
        fs.rmSync(binary, { force: true });
        fs.renameSync(binaryBackup, binary);
        fs.rmSync(manifest, { force: true });
        if (fs.existsSync(manifestBackup)) fs.renameSync(manifestBackup, manifest);
        if (prior) await launchAndVerify(prior.executable === binary ? binary : prior.executable, prior.version, wait);
      } catch (rollbackError) {
        throw new Error(`${failure} Rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
      }
      throw new Error(`${failure} Previous Meta-Proxy ${prior ? 'was restored and verified' : 'binary was restored; it was not running before the upgrade'}.`);
    }
    throw error;
  } finally {
    fs.rmSync(binaryBackup, { force: true });
    fs.rmSync(manifestBackup, { force: true });
    release?.();
  }
}

View on GitHub (pinned to 29f048fc3b)

Solutions

  1. Read both messages in the error: the first clause is why the upgrade failed, the 'Rollback failed' clause is why restore failed — fix the rollback cause first (often relaunch timeout)
  2. Manually restore: move <binary>.rollback back over the binary (and manifest if present), then start meta-proxy by hand and verify /version
  3. Check the daemon log (proxyLogFilePath()) and free resources (disk space, file locks) before retrying the install
  4. On Windows, close processes holding meta-proxy.exe (tasklist /m, or a reboot) so the rename can succeed, then retry

Example fix

// manual recovery after rollback failure
mv ~/.metaharness/bin/meta-proxy.rollback ~/.metaharness/bin/meta-proxy
~/.metaharness/bin/meta-proxy &
Defensive patterns

Strategy: try-catch

Validate before calling

// Snapshot state so you can recover manually if rollback itself fails
import { createHash } from 'node:crypto';
const bin = proxyBinaryPath();
const sha = fs.existsSync(bin) ? createHash('sha256').update(fs.readFileSync(bin)).digest('hex') : null;
fs.writeFileSync('/tmp/meta-proxy-preupgrade.json', JSON.stringify({ bin, sha, ts: Date.now() }));

Type guard

null

Try / catch

try {
  await installAndActivateProxy(version);
} catch (e) {
  if (e instanceof Error && e.message.includes('Rollback failed')) {
    // both causes are embedded: root failure + rollback failure; recover manually
    console.error(e.message);
    console.error(`Manually restore ${proxyBinaryPath()}.rollback if present`);
  } else throw e;
}

Prevention

When it happens

Trigger: An install failure occurs with a backup present (e.g. launchAndVerify of the new version failed), and then rollback also fails — commonly the relaunch of the prior daemon times out (prior exit didn't complete / port busy), or file restore fails (EBUSY/EPERM on Windows, read-only filesystem).

Common situations: Old daemon can't restart because its binary was partially replaced; Windows file locks prevent rename while a process holds the .exe; disk full during restore; the pre-upgrade daemon can't come back up for the same environmental reasons the new one failed (config, permissions, supervisor interference).

Related errors


AI-assisted analysis of ruvnet/ruflo@29f048fc3b (2026-09-01). Data as JSON: /api/errors/6e35a4b9fca6eea4. Report an issue: GitHub.