ruvnet/ruflo · error

Rollout not found: ${rolloutId}

Error message

Rollout not found: ${rolloutId}

What it means

EvolutionPipeline.advanceStage() moves a staged rollout to its next stage or auto-rolls back when divergence exceeds the stage threshold. Rollouts live in the in-memory `rollouts` Map and their IDs are only minted by stage(), so this error means the rolloutId was never issued by this pipeline instance (or was lost to a restart). It is checked before the in-progress status check, so an unknown ID always throws rather than returning a reason object.

Source

Thrown at v3/@claude-flow/guidance/src/evolution.ts:505

  }

  // ==========================================================================
  // Advance Stage
  // ==========================================================================

  /**
   * Advance to the next rollout stage or auto-rollback.
   *
   * If `stageMetrics.divergence` exceeds the current stage's threshold,
   * the rollout is automatically rolled back.
   */
  advanceStage(
    rolloutId: string,
    stageMetrics: Record<string, number>,
  ): { advanced: boolean; rolledBack: boolean; reason: string } {
    const rollout = this.rollouts.get(rolloutId);
    if (!rollout) {
      throw new Error(`Rollout not found: ${rolloutId}`);
    }

    if (rollout.status !== 'in-progress') {
      return {
        advanced: false,
        rolledBack: false,
        reason: `Rollout is ${rollout.status}, not in-progress`,
      };
    }

    const current = rollout.stages[rollout.currentStage];
    const now = Date.now();

    // Record metrics on the current stage
    current.metrics = { ...stageMetrics };

    // Check divergence against threshold
    const divergence = stageMetrics.divergence ?? 0;

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Use the rolloutId captured from `const rollout = pipeline.stage(proposalId)`
  2. Guard with `if (!pipeline.getRollout(rolloutId)) return;` before advancing
  3. Re-create the rollout (stage again on a live proposal) after a restart
  4. Keep stage()/advanceStage() calls on one long-lived pipeline instance

Example fix

// before
const r = pipeline.advanceStage(savedRolloutId, metrics); // throws after restart

// after
const existing = pipeline.getRollout(savedRolloutId);
if (existing) {
  const r = pipeline.advanceStage(savedRolloutId, metrics);
} else {
  const rollout = pipeline.stage(liveProposalId); // re-stage, then advance
  const r = pipeline.advanceStage(rollout.rolloutId, metrics);
}
Defensive patterns

Strategy: validation

Validate before calling

if (pipeline.getRollout(rolloutId) === undefined) {
  // rollout unknown to this instance — re-stage before advancing
}

Try / catch

try {
  const r = pipeline.advanceStage(rolloutId, metrics);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Rollout not found')) {
    // restart wiped rollouts — re-stage the proposal
  } else throw err;
}

Prevention

When it happens

Trigger: Calling `advanceStage(rolloutId, metrics)` with an ID not returned by stage(); pipeline restarted between staging and advancing (Map lost); advancing on a different pipeline instance than the one that staged; copying the rolloutId from logs with a typo.

Common situations: Long-running canary jobs that sleep between stages and survive a redeploy; metrics-collection workers calling advanceStage from a separate process; replaying operational runbooks against a fresh pipeline.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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