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

  1. List the minimal set of tools the pod genuinely needs, e.g. ["memory_store"]
  2. If the pod truly needs zero MCP tools, that configuration is not supported — reconsider whether a pod template is the right vehicle
  3. 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

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


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