ruvnet/ruflo · error · Error

unsupported calibrator schema v=${j?.v}

Error message

unsupported calibrator schema v=${j?.v}

What it means

Thrown by IsotonicCalibrator.fromJSON(j) when j is null/undefined OR j.v !== 1. The calibrator file format is versioned with a single schema version (v:1, holding a buckets array); any other version, or a payload missing the v field, is rejected. This guards against silently applying a calibrator fit for a different schema — which would produce garbage predictedQuality values in the router.

Source

Thrown at v3/@claude-flow/cli/src/ruvector/router-calibrator.ts:128

    // Linear interpolation between two adjacent bucket midpoints. Could be
    // binary-searched, but n is typically <30 after PAV so linear is fine.
    for (let i = 0; i < n - 1; i++) {
      if (x >= mids[i] && x <= mids[i + 1]) {
        const t = (x - mids[i]) / (mids[i + 1] - mids[i]);
        return this.buckets[i].calibrated * (1 - t) + this.buckets[i + 1].calibrated * t;
      }
    }
    return x; // unreachable given the bounds above
  }

  /** Pure-JSON serialization — calibrator JSON is small (typically <2kB). */
  toJSON(): CalibratorJSON {
    return { v: 1, buckets: this.buckets };
  }

  static fromJSON(j: CalibratorJSON): IsotonicCalibrator {
    if (!j || j.v !== 1) throw new Error(`unsupported calibrator schema v=${j?.v}`);
    return new IsotonicCalibrator(j.buckets);
  }

  /** Diagnostic — number of distinct calibration points after PAV. */
  get bucketCount(): number {
    return this.buckets.length;
  }

  /** Diagnostic — return a copy of the bucket array (read-only view). */
  inspect(): CalibratorBucket[] {
    return this.buckets.map(b => ({ ...b }));
  }
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Inspect the file at cfg.calibratorPath — it must contain {"v":1,"buckets":[...]}.
  2. Regenerate the calibrator with IsotonicCalibrator.fit(...).toJSON() on the current version and overwrite the file.
  3. If the file is from an incompatible version, delete it and disable calibration (CLAUDE_FLOW_ROUTER_CALIBRATE=0) until a matching one is produced.
  4. Guard fromJSON calls so a bad calibrator disables calibration rather than crashing router init — loadCal in neural-router already does this (catch returns null).

Example fix

// before — direct fromJSON on untrusted payload
const cal = IsotonicCalibrator.fromJSON(JSON.parse(raw));

// after — schema-check first, fall back to no-calibration
let cal = null;
try {
  const j = JSON.parse(raw);
  if (j && j.v === 1) cal = IsotonicCalibrator.fromJSON(j);
} catch { /* leave cal null */ }
const router = cal ? wrapWithCalibrator(r, cal) : r;
Defensive patterns

Strategy: validation

Validate before calling

import { IsotonicCalibrator } from './router-calibrator';

function loadCalibratorSafe(path: string) {
  try {
    const j = JSON.parse(readFileSync(path, 'utf8'));
    if (!j || j.v !== 1) return null;       // wrong schema → disable calibration
    return IsotonicCalibrator.fromJSON(j);
  } catch {
    return null;
  }
}

Type guard

function isCalibratorJSONv1(j: unknown): j is { v: 1; buckets: unknown[] } {
  return !!j && typeof j === 'object' && (j as any).v === 1 && Array.isArray((j as any).buckets);
}

Try / catch

let calibrator = null;
try {
  calibrator = IsotonicCalibrator.fromJSON(parsed);
} catch (e) {
  if (/unsupported calibrator schema/.test(String(e))) {
    // Disable calibration rather than crashing router init.
    console.warn('calibrator schema mismatch — running uncalibrated');
  } else throw e;
}

Prevention

When it happens

Trigger: Loading a calibrator JSON written by a future version (v:2); loading a partial/empty file ({}, null); loading a file that's actually a different JSON (e.g. the KRR artifact) by mistake; the file was hand-edited and the v field was removed; JSON.parse on an empty file returns null and that's passed in.

Common situations: Cross-version install: calibrator was generated by a newer @claude-flow/cli and loaded by an older one (or vice versa); a path misconfiguration points calibratorPath at the wrong JSON; CI copied a stale calibrator from an old release; the calibrator file got truncated to empty during a write.

Related errors


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