ruvnet/ruflo · error · DarwinUnavailableError

Darwin failed closed: ${(error as Error).message}

Error message

Darwin failed closed: ${(error as Error).message}

What it means

If the darwinInvoker throws something other than DarwinUnavailableError, the catch block wraps the original message as DarwinUnavailableError('Darwin failed closed: <original>'). In mode 'darwin' this rethrows (the caller demanded Darwin and must see the failure); in other modes it would fall back to the local proposer with a 'darwin-error:...' substitution. 'Failed closed' signals an unexpected Darwin-side fault that the proposer will not silently mask.

Source

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

      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,
    });
  } catch (error) {
    if (input.mode === 'darwin') {
      if (error instanceof DarwinUnavailableError) throw error;
      throw new DarwinUnavailableError(`Darwin failed closed: ${(error as Error).message}`);
    }
    return runLocal(`darwin-error:${(error as Error).message}`);
  }
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Read the wrapped message: the original error's text is preserved after 'Darwin failed closed:'.
  2. Stabilize the Darwin backend (network, service health) and retry.
  3. If Darwin is unreliable, run with mode 'auto' so failures fall back to local instead of surfacing.
  4. Harden the darwinInvoker to translate known transient errors into retries or DarwinUnavailableError with a clear reason.

Example fix

// before
try { await proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, ... }); }
catch (e) { /* 'Darwin failed closed: <x>' — surface to user */ }

// after — fall back when Darwin is flaky
const archive = await proposeFlywheelCandidates({ mode: 'auto', darwinInvoker, ... });
// archive.effective === 'local', archive.substitution === 'darwin-error:<x>' on failure
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate by health-checking Darwin before the run:
if (darwinInvoker && !(await isDarwinHealthy(darwinInvoker))) {
  // run local instead, or surface a clear message
  await proposeFlywheelCandidates({ mode: 'local', ... });
} else {
  await proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, ... });
}

Try / catch

try {
  return await proposeFlywheelCandidates({ mode: 'darwin', darwinInvoker, ... });
} catch (e) {
  if (e instanceof DarwinUnavailableError) {
    // original cause is in the message after 'Darwin failed closed:'
    return await proposeFlywheelCandidates({ mode: 'local', ... });
  }
  throw e;
}

Prevention

When it happens

Trigger: The darwinInvoker rejects with an arbitrary error (network failure, serialization error, unhandled exception inside Darwin, timeout from a non-budget source) that is not already a DarwinUnavailableError.

Common situations: Darwin service is unreachable (DNS/connection refused); Darwin threw a TypeError/RangeError internally; the invoker's response failed schema parsing; an upstream dependency of Darwin crashed.

Related errors


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