ruvnet/ruflo · error

selfConsistency: aggregator='mean' requires every sample to

Error message

selfConsistency: aggregator='mean' requires every sample to be a finite number

What it means

When aggregator is 'mean', selfConsistency() reduces all N samples numerically and requires every sample to be a finite number; one bad sample out of N aborts the aggregation. Non-numbers (strings, undefined, null, objects) and non-finite numbers (NaN, Infinity, -Infinity) are all rejected via an every() check before the mean/variance computation.

Source

Thrown at v3/@claude-flow/neural/src/utils/self-consistency.ts:102

    // arrays). Float32Array does NOT JSON-encode losslessly by default —
    // callers wanting f32 majority should pre-convert via Array.from.
    const counts = new Map<string, { value: T; count: number }>();
    for (const s of samples) {
      const key = canonicalKey(s);
      const existing = counts.get(key);
      if (existing) existing.count += 1;
      else counts.set(key, { value: s, count: 1 });
    }
    let best: { value: T; count: number } = { value: samples[0], count: 0 };
    for (const c of counts.values()) {
      if (c.count > best.count) best = c;
    }
    finalAnswer = best.value;
    agreement = best.count / samples.length;
  } else if (aggregator === 'mean') {
    const nums = samples as unknown as number[];
    if (!nums.every((v) => typeof v === 'number' && Number.isFinite(v))) {
      throw new Error("selfConsistency: aggregator='mean' requires every sample to be a finite number");
    }
    const mean = nums.reduce((a, b) => a + b, 0) / nums.length;
    const variance = nums.reduce((s, v) => s + (v - mean) ** 2, 0) / nums.length;
    const stddev = Math.sqrt(variance);
    const range = Math.max(1e-9, Math.abs(mean) || 1); // avoid div-by-0
    finalAnswer = mean as unknown as T;
    agreement = Math.max(0, Math.min(1, 1 - stddev / range));
  } else {
    finalAnswer = samples[0];
    agreement = 1;
  }

  return { finalAnswer, samples, agreement, config };
}

/**
 * Canonical-form key for grouping. JSON.stringify is enough for primitives,
 * arrays, and plain objects with stable key order. Callers wanting locale-

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Normalize inside the operation: coerce with Number(x) and check Number.isFinite, or throw your own domain error instead of letting a non-number flow through
  2. Filter or replace failed samples (e.g. resolve to a sentinel you drop) before they reach the aggregator
  3. Switch aggregator to 'majority' when outputs are not strictly numeric
  4. Add a unit test asserting the operation always returns finite numbers

Example fix

// before
await selfConsistency(() => model.sample(), { N: 5, aggregator: 'mean' });
// after
await selfConsistency(async () => {
  const out = await model.sample();
  const n = Number(out.score);
  if (!Number.isFinite(n)) throw new RangeError('sample is not a finite number');
  return n;
}, { N: 5, aggregator: 'mean' });
Defensive patterns

Strategy: type-guard

Validate before calling

// make the operation itself safe so every sample is a finite number
const safeOp = async () => {
  const out = await operation();
  const n = Number(out);
  if (!Number.isFinite(n)) throw new RangeError('sample is not a finite number');
  return n;
};
await selfConsistency(safeOp, { N: 5, aggregator: 'mean' });

Type guard

function isFiniteNumber(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v);
}

Try / catch

try {
  await selfConsistency(op, { N, aggregator: 'mean' });
} catch (e) {
  if (String(e).includes("aggregator='mean'")) {
    // fall back to majority vote over the same samples
    await selfConsistency(op, { N, aggregator: 'majority' });
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: The operation returns mixed types — a number on success but a string or undefined on a parse failure or missing field; a model call that occasionally yields NaN after Number('N/A'); a division-by-zero branch leaking Infinity into one sample.

Common situations: Applying 'mean' to LLM/model outputs without normalizing them to numbers first; aggregation configs copied from a majority-vote use case; samples drawn from heterogeneous optional fields.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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