ruvnet/ruflo · error · PodTemplateValidationError

field "${key}" must be a finite number

Error message

field "${key}" must be a finite number

What it means

Pod template validator's requireNumber() throws PodTemplateValidationError when a required numeric field is missing, not a number, or non-finite (NaN/Infinity). Applies to fields like budgetUsdMonthly, budgetUsdPerRun, bench.scheduleHours, and auditReadView.retentionDays. JSON cannot legally encode NaN/Infinity, so non-finite here means a string, boolean, null, or missing value.

Source

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

const PII_POLICIES: PiiPolicy[] = ['soc2', 'gdpr', 'hipaa', 'permissive'];

function isObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

function requireString(parent: Record<string, unknown>, key: string, path: string): string {
  const v = parent[key];
  if (typeof v !== 'string' || v.length === 0) {
    throw new PodTemplateValidationError(`field "${key}" must be a non-empty string`, path);
  }
  return v;
}

function requireNumber(parent: Record<string, unknown>, key: string, path: string): number {
  const v = parent[key];
  if (typeof v !== 'number' || !Number.isFinite(v)) {
    throw new PodTemplateValidationError(`field "${key}" must be a finite number`, path);
  }
  return v;
}

function requireBoolean(parent: Record<string, unknown>, key: string, path: string): boolean {
  const v = parent[key];
  if (typeof v !== 'boolean') {
    throw new PodTemplateValidationError(`field "${key}" must be a boolean`, path);
  }
  return v;
}

function requireArray<T>(parent: Record<string, unknown>, key: string, path: string,
                          itemValidator: (item: unknown, ipath: string) => T): T[] {
  const v = parent[key];
  if (!Array.isArray(v)) {
    throw new PodTemplateValidationError(`field "${key}" must be an array`, path);
  }

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Make the named field an unquoted JSON number: "budgetUsdMonthly": 100 not "100"
  2. If generating templates programmatically, coerce with Number() and validate before writing
  3. Check the error's path to find nested numeric fields (bench.scheduleHours, auditReadView.retentionDays)
  4. Lint templates in CI with the same validator to catch regressions

Example fix

// before
{ "budgetUsdMonthly": "100", "budgetUsdPerRun": "2" }
// after
{ "budgetUsdMonthly": 100, "budgetUsdPerRun": 2 }
Defensive patterns

Strategy: validation

Validate before calling

function isFiniteNumberField(o: Record<string, unknown>, key: string): boolean {
  return typeof o[key] === 'number' && Number.isFinite(o[key] as number);
}
for (const k of ['budgetUsdMonthly', 'budgetUsdPerRun']) {
  if (!isFiniteNumberField(template, k)) fail(`${k} must be a bare JSON number`);
}

Type guard

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

Try / catch

try {
  validatePodTemplate(template);
} catch (e) {
  if (e instanceof PodTemplateValidationError && e.message.includes('finite number')) {
    fail(`Unquote the numeric field at ${e.path}`);
  } else throw e;
}

Prevention

When it happens

Trigger: validatePodTemplate() with a numeric field supplied as a string ("budgetUsdMonthly": "100"), left undefined, set to null/true, or parsed from YAML/JSON5 where unquoted NaN sneaks in. The failing key name appears in the message.

Common situations: Templates authored in YAML then converted to JSON, where numeric-looking strings stay quoted; config management (helm values, terraform jsonencode) injecting everything as strings; template generated from user form input without coercion.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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