ruvnet/ruflo · error · PodTemplateValidationError
allowedMcpTools must have ≥1 entry
Error message
allowedMcpTools must have ≥1 entry
What it means
After all allowedMcpTools entries pass the per-entry string check, validatePodTemplate() rejects an empty array. The tool allowlist is deny-by-default, so an empty list would silently block every MCP call; the schema instead forces you to explicitly name at least one permitted tool.
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:217
'roomId may only contain [A-Za-z0-9_.\\-:/@#]',
'/',
);
}
const agents = requireArray(json, 'agents', '/', validatePodAgent);
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) {View on GitHub (pinned to fa13ee4ad6)
Solutions
- List the minimal set of tools the pod genuinely needs, e.g. ["memory_store"]
- If the pod truly needs zero MCP tools, that configuration is not supported — reconsider whether a pod template is the right vehicle
- Regenerate the allowlist from the agent's actual tool usage logs to get a non-empty, accurate set
Example fix
// before "allowedMcpTools": [] // after "allowedMcpTools": ["memory_store"]
Defensive patterns
Strategy: try-catch
Validate before calling
if (!Array.isArray(json.allowedMcpTools) || json.allowedMcpTools.length === 0) {
throw new Error('enumerate at least one MCP tool in allowedMcpTools');
} Type guard
function hasNonEmptyToolAllowlist(v: unknown): boolean {
const a = (v as { allowedMcpTools?: unknown[] })?.allowedMcpTools;
return Array.isArray(a) && a.length > 0;
} Try / catch
try { validatePodTemplate(json); } catch (err) {
if (err instanceof PodTemplateValidationError && /allowedMcpTools must have/.test(err.message)) {
// deny-by-default: 'no tools' is not expressible — add the minimal tool set
}
} Prevention
- Derive the allowlist from observed tool-usage logs so it is never empty
- Review lockdown edits that strip all tools from a template
When it happens
Trigger: A template with "allowedMcpTools": []. (An empty-string or non-string entry would have thrown error 188 first; this error means every entry was a valid non-empty string but there are none.)
Common situations: Templates intended to be fully sandboxed ('no tools') — not expressible, you must enumerate at least one tool; allowlist generation that filters aggressively and produces an empty set; removing tools during lockdown hardening.
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
- allowedMcpTools entries must be non-empty strings
- bench.scheduleHours must be ≥1
- auditReadView must be an object
- includedEventTypes entries must be non-empty strings
- auditReadView.retentionDays must be ≥1
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/97390d4cc920e098.
Report an issue: GitHub.