ruvnet/ruflo · error · PodTemplateValidationError
piiPolicy must be one of: ${PII_POLICIES.join(', ')}
Error message
piiPolicy must be one of: ${PII_POLICIES.join(', ')} What it means
The top-level piiPolicy string must be one of the closed set defined in PII_POLICIES: 'soc2', 'gdpr', 'hipaa', 'permissive' (the ${PII_POLICIES.join(', ')} in the message is interpolated at throw time to exactly that list). Any other value — including case variants like 'SOC2' — is rejected at path '/'.
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:222
if (agents.length === 0) {
throw new PodTemplateValidationError('agents must have ≥1 entry', '/');
}
const allowedMcpTools = requireArray(json, 'allowedMcpTools', '/', (t, tp) => {
if (typeof t !== 'string' || t.length === 0) {
throw new PodTemplateValidationError(
'allowedMcpTools entries must be non-empty strings',
tp,
);
}
return t;
});
if (allowedMcpTools.length === 0) {
throw new PodTemplateValidationError('allowedMcpTools must have ≥1 entry', '/');
}
const bench = validatePodBench(json.bench, '/bench');
const piiPolicy = requireString(json, 'piiPolicy', '/');
if (!PII_POLICIES.includes(piiPolicy as PiiPolicy)) {
throw new PodTemplateValidationError(
`piiPolicy must be one of: ${PII_POLICIES.join(', ')}`,
'/',
);
}
const budgetUsdMonthly = requireNumber(json, 'budgetUsdMonthly', '/');
if (budgetUsdMonthly < 0) {
throw new PodTemplateValidationError('budgetUsdMonthly must be ≥0', '/');
}
const budgetUsdPerRun = requireNumber(json, 'budgetUsdPerRun', '/');
if (budgetUsdPerRun < 0) {
throw new PodTemplateValidationError('budgetUsdPerRun must be ≥0', '/');
}
if (budgetUsdMonthly > 0 && budgetUsdPerRun > budgetUsdMonthly) {
throw new PodTemplateValidationError(
'budgetUsdPerRun must not exceed budgetUsdMonthly',
'/',
);
}View on GitHub (pinned to fa13ee4ad6)
Solutions
- Set piiPolicy to exactly one of: soc2, gdpr, hipaa, permissive (lowercase)
- If unsure which applies, 'permissive' is the least restrictive valid value — confirm with your compliance requirements
- Guard in your template generator with an allowed-set check so invalid values never reach validation
Example fix
// before "piiPolicy": "SOC2" // after "piiPolicy": "soc2"
Defensive patterns
Strategy: validation
Validate before calling
const PII = ['soc2', 'gdpr', 'hipaa', 'permissive'] as const; if (!PII.includes(template.piiPolicy)) template.piiPolicy = 'permissive'; // or reject explicitly
Type guard
const PII_POLICIES = ['soc2', 'gdpr', 'hipaa', 'permissive'] as const;
function isPiiPolicy(v: unknown): v is typeof PII_POLICIES[number] {
return typeof v === 'string' && (PII_POLICIES as readonly string[]).includes(v);
} Try / catch
try { validatePodTemplate(json); } catch (err) {
if (err instanceof PodTemplateValidationError && /piiPolicy/.test(err.message)) {
// message lists the exact allowed values — lowercase and retry
}
} Prevention
- Lowercase policy names at the config boundary
- Keep the enum in one shared constant instead of re-typing it in templates
When it happens
Trigger: A template with piiPolicy set to "SOC2", "GDPR", "none", "strict", "eu-gdpr", or a typo like "sox2". requireString already guaranteed it is a string, so the failure is purely an unrecognized enum value.
Common situations: Uppercase policy names copied from compliance docs; 'none'/'strict' guesses by authors who never saw the enum; regional aliases ('eu-gdpr') from internal config systems.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- bench.scheduleHours must be ≥1
- auditReadView must be an object
- includedEventTypes entries must be non-empty strings
- auditReadView.retentionDays must be ≥1
- roomId may only contain [A-Za-z0-9_.\-:/@#]
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/04d2d858a711a3bd.
Report an issue: GitHub.