ruvnet/ruflo · error

selfConsistency: N must be a positive integer, got ${config.

Error message

selfConsistency: N must be a positive integer, got ${config.N}

What it means

selfConsistency() validates its sample count up front: config.N must be a positive integer (Number.isInteger true and > 0). Zero, negatives, fractional values like 2.5, NaN, and Infinity all fail before the operation is run even once, so no samples are wasted.

Source

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

   * For 'majority': fraction of samples matching finalAnswer (0..1).
   * For 'mean':    1 - normalized stddev (rough confidence proxy).
   * For 'first':   always 1.
   */
  agreement: number;
  /** The config used for this run. */
  config: SelfConsistencyConfig;
}

/**
 * Run an operation N times and aggregate. The operation is awaited
 * sequentially — if the caller wants concurrency they can wrap it themselves.
 */
export async function selfConsistency<T>(
  operation: () => Promise<T> | T,
  config: SelfConsistencyConfig,
): Promise<SelfConsistencyResult<T>> {
  if (!Number.isInteger(config.N) || config.N <= 0) {
    throw new Error(`selfConsistency: N must be a positive integer, got ${config.N}`);
  }

  const samples: T[] = [];
  for (let i = 0; i < config.N; i++) {
    samples.push(await operation());
  }

  const aggregator: SelfConsistencyAggregator = config.aggregator ?? 'majority';

  let finalAnswer: T;
  let agreement: number;

  if (aggregator === 'majority') {
    // Group by JSON-stringified value (handles primitives, plain objects,
    // 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) {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Default N explicitly at the call site: config.N ?? 3
  2. Coerce computed values with Math.max(1, Math.round(n)) before building the config
  3. Validate any user-supplied N (CLI arg, env var, API field) before it reaches selfConsistency
  4. Fail fast in your own config loader with a clear message instead of letting the library reject it later

Example fix

// before
await selfConsistency(op, { N: ratio * 10, aggregator: 'mean' });
// after
const N = Math.max(1, Math.round(ratio * 10));
await selfConsistency(op, { N, aggregator: 'mean' });
Defensive patterns

Strategy: validation

Validate before calling

const N = Number.isInteger(cfg.N) && (cfg.N as number) > 0 ? cfg.N : 3;
await selfConsistency(op, { ...cfg, N });

Type guard

function isPositiveInteger(n: unknown): n is number {
  return typeof n === 'number' && Number.isInteger(n) && n > 0;
}

Prevention

When it happens

Trigger: Passing N: 0 because a config default was never set; computing N from a ratio that yields a float (e.g. 3 * 0.5); NaN from Number.parseInt(undefined) on a missing CLI/env flag; a negative result from a subtraction like budget - used.

Common situations: CLI- or env-driven sampling counts where a missing flag coerces to 0 or NaN; dynamic N = Math.round(quality * k) with quality 0; configs copied between callers with different N semantics.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


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