ruvnet/ruflo · error · PodTemplateValidationError

pod-template at ${path}: includedEventTypes entries must be

Error message

pod-template at ${path}: includedEventTypes entries must be non-empty strings

What it means

Thrown inside validateAuditReadView() while validating each element of the includedEventTypes array. Fires when an entry is not a string or is an empty string. includedEventTypes is the allow-list of audit event type names (e.g., 'reservation.committed') surfaced to the business-owner read view.

Source

Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:162

    return s;
  });
  if (successCriteria.length === 0) {
    throw new PodTemplateValidationError('bench.successCriteria must have ≥1 entry', path);
  }
  const scheduleHours = requireNumber(item, 'scheduleHours', path);
  if (scheduleHours < 1) {
    throw new PodTemplateValidationError('bench.scheduleHours must be ≥1', path);
  }
  return { name, description, successCriteria, scheduleHours };
}

function validateAuditReadView(item: unknown, path: string): PodAuditReadView {
  if (!isObject(item)) {
    throw new PodTemplateValidationError('auditReadView must be an object', path);
  }
  const includedEventTypes = requireArray(item, 'includedEventTypes', path, (s, sp) => {
    if (typeof s !== 'string' || s.length === 0) {
      throw new PodTemplateValidationError('includedEventTypes entries must be non-empty strings', sp);
    }
    return s;
  });
  const retentionDays = requireNumber(item, 'retentionDays', path);
  if (retentionDays < 1) {
    throw new PodTemplateValidationError('auditReadView.retentionDays must be ≥1', path);
  }
  return { includedEventTypes, retentionDays };
}

// POSIX cron — five or six space-separated fields. Permissive on field
// contents (digits, *, -, /, ,) — actual cron evaluation happens at schedule
// time. We only catch obviously malformed values here.
const CRON_RE = /^([\d*/,\-]+\s+){4,5}[\d*/,\-]+$/;

/**
 * Validate `json` and return a typed `PodTemplate`. Throws
 * `PodTemplateValidationError` with a JSON-pointer-style path on failure.

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Read the error path — it includes the index (e.g., /auditReadView/includedEventTypes[0]) of the bad entry
  2. Ensure every entry is a non-empty event-type string matching the BudgetAuditSink emit event names (e.g., 'reservation.committed', 'reservation.released')

Example fix

// before:
"includedEventTypes": ["reservation.committed", ""] // empty, throws

// after:
"includedEventTypes": ["reservation.committed", "reservation.budget_exceeded"]
Defensive patterns

Strategy: validation

Validate before calling

import { validatePodTemplate, PodTemplateValidationError } from '@claude-flow/cli/business-pods/pod-schema';

try {
  const pod = validatePodTemplate(parsedJson);
} catch (e) {
  if (e instanceof PodTemplateValidationError) {
    // e.path includes the index, e.g. /auditReadView/includedEventTypes[0]
    console.error(`includedEventTypes entry error at ${e.path}: ${e.message}`);
  }
}

Try / catch

import { validatePodTemplate, PodTemplateValidationError } from '@claude-flow/cli/business-pods/pod-schema';

try {
  const pod = validatePodTemplate(raw);
} catch (e) {
  if (e instanceof PodTemplateValidationError) {
    console.error(e.message);
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: validateAuditReadView() runs requireArray() on auditReadView.includedEventTypes, then maps each entry through a validator; an entry is not a string or is empty.

Common situations: An event-type entry is null, a number, an empty placeholder string, or a typo leaving an unfilled template variable (e.g., "${EVENT_TYPE}").

Related errors


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