ruvnet/ruflo · error · PodTemplateValidationError
roomId may only contain [A-Za-z0-9_.\-:/@#]
Error message
roomId may only contain [A-Za-z0-9_.\-:/@#]
What it means
The top-level roomId must match /^[A-Za-z0-9_.\-:/@#]+$/: only letters, digits, underscore, dot, hyphen, colon, slash, at-sign, and hash. Spaces, commas, percent signs, pluses, quotes, and any non-ASCII characters are rejected. This charset matches the room/namespace identifiers used by the coordination transport, where other characters would break routing or shell safety.
Solutions
- Replace spaces with hyphens or use the canonical room ID ("team-sales" or "#team-sales:example.org")
- Trim the value and re-check for invisible characters (smart quotes, non-breaking spaces) — retype it if unsure
- URL-decode the value first if it arrived percent-encoded, then confirm every remaining char is in the allowed set
Example fix
// before "roomId": "team sales room" // after "roomId": "team-sales"
Defensive patterns
Strategy: validation
Validate before calling
const ROOM_ID_RE = /^[A-Za-z0-9_.\-:/@#]+$/;
if (!ROOM_ID_RE.test(template.roomId)) {
template.roomId = template.roomId.trim().replace(/\s+/g, '-');
} Type guard
function isSafeRoomId(v: unknown): v is string {
return typeof v === 'string' && /^[A-Za-z0-9_.\-:/@#]+$/.test(v);
} Try / catch
try { validatePodTemplate(json); } catch (err) {
if (err instanceof PodTemplateValidationError && /roomId/.test(err.message)) {
// sanitize: decodeURIComponent first, then replace disallowed chars
}
} Prevention
- Store the canonical room ID in your inventory, never paste display titles
- URL-decode identifiers that traveled through query strings before embedding them
When it happens
Trigger: A template with roomId containing spaces ("team sales"), percent-encoded characters ("team%20sales"), a plus sign from URL encoding, or emoji/unicode pasted from a chat app.
Common situations: Pasting a chat room title instead of its ID/alias; copy-paste introducing a trailing space or smart quote; using URL-encoded forms of the room identifier.
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
- agents must have ≥1 entry
- allowedMcpTools entries must be non-empty strings
- allowedMcpTools must have ≥1 entry
- auditReadView must be an object
- auditReadView.retentionDays must be ≥1
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/b5f900cf6e2bdf04.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:198
* `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,
);
}
return t;
});
if (allowedMcpTools.length === 0) {View on GitHub (pinned to fa13ee4ad6)