ruvnet/ruflo · error · DarwinUnavailableError

Darwin archive exceeded candidate-count budget

Error message

Darwin archive exceeded candidate-count budget

What it means

After a completed Darwin run, proposeFlywheelCandidates checks that result.candidates.length does not exceed maxCandidates (default 256). Exceeding it means Darwin produced more candidates than the caller budgeted for, which could blow downstream evaluation cost, so it throws DarwinUnavailableError.

Source

Thrown at v3/@claude-flow/cli/src/services/flywheel-proposer.ts:225

        budget: {
          maxEvaluationCostMicros: input.safetyEnvelope.maxEvaluationCostMicros,
          maxConcurrency: input.maxConcurrency ?? 2,
          maxWallTimeMs,
        },
      }),
      new Promise<never>((_, reject) => {
        timeout = setTimeout(() => reject(new DarwinUnavailableError('Darwin exceeded wall-time budget')), maxWallTimeMs);
        timeout.unref?.();
      }),
    ]).finally(() => {
      if (timeout) clearTimeout(timeout);
    });
    if (!result.completed) {
      if (input.mode === 'darwin') throw new DarwinUnavailableError(result.reason ?? 'Darwin archive incomplete');
      return runLocal(`darwin-incomplete:${result.reason ?? 'unknown'}`);
    }
    if (result.candidates.length > (input.maxCandidates ?? 256)) {
      throw new DarwinUnavailableError('Darwin archive exceeded candidate-count budget');
    }
    const evaluationCost = result.candidates.reduce(
      (sum, candidate) => sum + candidate.resources.evaluationCostMicros,
      0,
    );
    if (evaluationCost > input.safetyEnvelope.maxEvaluationCostMicros) {
      throw new DarwinUnavailableError('Darwin archive exceeded evaluation-cost budget');
    }
    return finalizeArchive({
      requested: input.mode,
      effective: 'darwin',
      promotionAllowed: true,
      baselineRef,
      safetyEnvelope: input.safetyEnvelope,
      seed: input.seed,
      candidates: result.candidates,
      completed: true,
    });

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Raise input.maxCandidates to a value Darwin will stay under, if your evaluation budget allows.
  2. Configure Darwin's own candidate-generation limit so its output fits within maxCandidates.
  3. Use mode 'auto' so a budget overrun falls back to the local proposer instead of throwing.
  4. If overruns are persistent, cap candidates in the darwinInvoker wrapper before returning.

Example fix

// before
proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, maxCandidates: 8, ... }); // throws if Darwin returns >8

// after — align budgets, or cap inside the invoker
const cappedInvoker = async (req) => {
  const r = await darwinInvoker(req);
  return { ...r, candidates: r.candidates.slice(0, 256) };
};
proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker: cappedInvoker, maxCandidates: 256, ... });
Defensive patterns

Strategy: validation

Validate before calling

const maxCandidates = 256; // align with what Darwin will produce
await proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, maxCandidates, ... });

Try / catch

try {
  return await proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, maxCandidates: 8, ... });
} catch (e) {
  if (e instanceof DarwinUnavailableError && /candidate-count budget/.test(e.message)) {
    return await proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, maxCandidates: 256, ... });
  }
  throw e;
}

Prevention

When it happens

Trigger: The Darwin backend returns more candidates than input.maxCandidates (or the 256 default), e.g. Darwin ignored the budget or the caller set maxCandidates lower than Darwin's natural output.

Common situations: maxCandidates set tightly (e.g. 8) while Darwin generates dozens; a Darwin version change increases default output; maxCandidates not coordinated with Darwin's own generation count.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/e21897db523bb2d9. Report an issue: GitHub.