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

  1. Replace spaces with hyphens or use the canonical room ID ("team-sales" or "#team-sales:example.org")
  2. Trim the value and re-check for invisible characters (smart quotes, non-breaking spaces) — retype it if unsure
  3. 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

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


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)