ruvnet/ruflo · error · PodTemplateValidationError

field "${key}" must be an array

Error message

field "${key}" must be an array

What it means

Pod template validator's requireArray() throws PodTemplateValidationError when a required array field is missing or not an array. Covers agents, allowedMcpTools, bench.successCriteria, and auditReadView.includedEventTypes. Item-level validation happens after this check, so a wrong container type fails here first.

Source

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

  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);
  }
  return v.map((item, idx) => itemValidator(item, `${path}/${key}[${idx}]`));
}

function validatePodAgent(item: unknown, path: string): PodAgent {
  if (!isObject(item)) throw new PodTemplateValidationError('agent must be an object', path);
  return {
    role: requireString(item, 'role', path),
    agentType: requireString(item, 'agentType', path),
    description: requireString(item, 'description', path),
    preferLocal: requireBoolean(item, 'preferLocal', path),
  };
}

function validatePodBench(item: unknown, path: string): PodBench {
  if (!isObject(item)) throw new PodTemplateValidationError('bench must be an object', path);
  const name = requireString(item, 'name', path);
  const description = requireString(item, 'description', path);

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Wrap values in JSON arrays: "allowedMcpTools": ["web_search"], "agents": [ {...} ]
  2. Use [] for an intentional empty list (note: bench.successCriteria additionally requires ≥1 entry)
  3. Check the named field at the error's path and fix its container type
  4. Run templates through validatePodTemplate in CI

Example fix

// before
{ "allowedMcpTools": "web_search" }
// after
{ "allowedMcpTools": ["web_search"] }
Defensive patterns

Strategy: validation

Validate before calling

function isStringArrayField(o: Record<string, unknown>, key: string): boolean {
  return Array.isArray(o[key]);
}
for (const k of ['agents', 'allowedMcpTools']) {
  if (!isStringArrayField(template, k)) fail(`${k} must be a JSON array, e.g. ["web_search"]`);
}

Type guard

function isUnknownArray(v: unknown): v is unknown[] {
  return Array.isArray(v);
}

Try / catch

try {
  validatePodTemplate(template);
} catch (e) {
  if (e instanceof PodTemplateValidationError && e.message.includes('must be an array')) {
    fail(`Wrap ${e.path} in [ ... ] — single values are not accepted`);
  } else throw e;
}

Prevention

When it happens

Trigger: validatePodTemplate() with "agents" as an object or string instead of a list, "allowedMcpTools": "*" (single string instead of ["*"]), successCriteria as a string, or the key missing. The failing key name is in the message.

Common situations: Single-item shorthand ("allowedMcpTools": "web_search" instead of ["web_search"]); YAML block scalars producing a string; templates edited by hand where brackets were dropped; null used to mean 'none'.

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/94907e32c4edb816. Report an issue: GitHub.