ruvnet/ruflo · error · Error

Cannot compute baseline from empty readings

Error message

Cannot compute baseline from empty readings

What it means

AnomalyDetectionService.computeBaseline derives per-dimension mean and standard deviation from a window of telemetry readings and needs at least one reading to establish dimensionality (readings[0].vector.length). An empty window is rejected up front rather than producing NaN statistics.

Source

Thrown at v3/@claude-flow/plugin-iot-cognitum/src/domain/services/anomaly-detection-service.ts:46

export class AnomalyDetectionService {
  private readonly config: AnomalyDetectionConfig;
  private readonly baselines = new Map<string, TelemetryBaseline>();

  constructor(config?: Partial<AnomalyDetectionConfig>) {
    this.config = {
      anomalyThreshold: config?.anomalyThreshold ?? 0.7,
      quarantineThreshold: config?.quarantineThreshold ?? 0.9,
      baselineWindowSize: config?.baselineWindowSize ?? 100,
    };
  }

  /**
   * Compute a baseline from a window of readings for a device.
   * Calculates mean and standard deviation per vector dimension.
   */
  computeBaseline(deviceId: string, readings: TelemetryReading[]): TelemetryBaseline {
    if (readings.length === 0) throw new Error('Cannot compute baseline from empty readings');

    const dim = readings[0].vector.length;
    const n = readings.length;

    // Compute mean
    const mean = new Array<number>(dim).fill(0);
    for (const r of readings) {
      for (let i = 0; i < dim; i++) mean[i] += r.vector[i] / n;
    }

    // Compute standard deviation
    const std = new Array<number>(dim).fill(0);
    for (const r of readings) {
      for (let i = 0; i < dim; i++) {
        const diff = r.vector[i] - mean[i];
        std[i] += (diff * diff) / n;
      }
    }

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Ingest at least one telemetry vector for the device before computing a baseline
  2. Guard the call: skip or defer baseline computation when readings.length === 0
  3. Verify the window bounds of the query include the device's actual telemetry range

Example fix

// before
const baseline = svc.computeBaseline(deviceId, await fetchReadings(deviceId, window)); // [] -> throws

// after
const readings = await fetchReadings(deviceId, window);
if (readings.length === 0) {
  logger.info(`no readings yet for ${deviceId}; baseline deferred`);
} else {
  const baseline = svc.computeBaseline(deviceId, readings);
}
Defensive patterns

Strategy: validation

Validate before calling

const readings = await fetchReadings(deviceId, window);
if (readings.length === 0) {
  return { status: 'deferred', reason: 'no readings in window' };
}
return svc.computeBaseline(deviceId, readings);

Type guard

function hasReadings(rs: TelemetryReading[]): rs is [TelemetryReading, ...TelemetryReading[]] {
  return rs.length > 0;
}

Try / catch

try {
  svc.computeBaseline(deviceId, readings);
} catch (e) {
  if (e instanceof Error && e.message === 'Cannot compute baseline from empty readings') {
    // defer baseline until telemetry arrives
  } else throw e;
}

Prevention

When it happens

Trigger: Calling computeBaseline with an empty readings array — typically a device that just registered and has no telemetry yet, or a time-window/baselineWindowSize query that matched zero readings.

Common situations: Baselining immediately after device onboarding before any ingest; querying a window that starts before the device's first telemetry timestamp; devices that were offline and accumulated no readings.

Related errors


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