ruvnet/ruflo · error · PodTemplateValidationError
name must be lowercase-kebab (e.g. "sales")
Error message
name must be lowercase-kebab (e.g. "sales")
What it means
The template's top-level name must match /^[a-z][a-z0-9-]*$/: start with a lowercase letter, followed only by lowercase letters, digits, and hyphens. Uppercase letters, underscores, leading digits/hyphens, spaces, and punctuation are rejected. The kebab name is the pod's stable identifier (used in scheduling, logs, and MCP tooling).
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:193
// time. We only catch obviously malformed values here.
const CRON_RE = /^([\d*/,\-]+\s+){4,5}[\d*/,\-]+$/;
/**
* Validate `json` and return a typed `PodTemplate`. Throws
* `PodTemplateValidationError` with a JSON-pointer-style path on failure.
*
* Used by:
* - `business_pod_validate` MCP tool — returns the error verbatim
* - `pod-tick.mjs` — pre-flight check before any pod execution
* - any external schema-loader that wants typed templates
*/
export function validatePodTemplate(json: unknown): PodTemplate {
if (!isObject(json)) {
throw new PodTemplateValidationError('pod-template must be a JSON object', '/');
}
const name = requireString(json, 'name', '/');
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
throw new PodTemplateValidationError('name must be lowercase-kebab (e.g. "sales")', '/');
}
const displayName = requireString(json, 'displayName', '/');
const roomId = requireString(json, 'roomId', '/');
if (!/^[A-Za-z0-9_.\-:/@#]+$/.test(roomId)) {
throw new PodTemplateValidationError(
'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,View on GitHub (pinned to fa13ee4ad6)
Solutions
- Rewrite name in lowercase-kebab: "sales", "growth-eng", "support-tier1"
- Keep the human label in displayName — that field has no format restriction
- Verify with a quick regex test before submitting the template to business_pod_validate
Example fix
// before
{ "name": "Sales_Pod", "displayName": "Sales" }
// after
{ "name": "sales-pod", "displayName": "Sales" } Defensive patterns
Strategy: validation
Validate before calling
if (!/^[a-z][a-z0-9-]*$/.test(template.name)) {
template.name = template.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+/, '');
} Type guard
function isKebabName(v: unknown): v is string {
return typeof v === 'string' && /^[a-z][a-z0-9-]*$/.test(v);
} Try / catch
try { validatePodTemplate(json); } catch (err) {
if (err instanceof PodTemplateValidationError && /lowercase-kebab/.test(err.message)) {
// slugify name, keep the original in displayName, retry
}
} Prevention
- Generate name from displayName with a slugify step in your template tooling
- Reserve human labels for displayName — name is a machine identifier
When it happens
Trigger: validatePodTemplate() on a template with name values like "Sales", "sales_pod", "1sales", "-sales", or "sales pod". The error is reported at path '/'.
Common situations: Copying the human-facing displayName ("Sales Pod") into name; org naming conventions with underscores; title-cased department names pasted from docs.
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
- bench.scheduleHours must be ≥1
- pod-template must be a JSON object
- 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/076887bc6edd9d4a.
Report an issue: GitHub.