{"record":{"id":"e818eb8e0ffd5e2b","repo":"ruvnet/ruflo","slug":"roomlabel-may-only-contain-a-za-z0-9","errorCode":null,"errorMessage":"roomLabel may only contain [A-Za-z0-9_.\\-:/@#]","messagePattern":"roomLabel may only contain \\[A-Za-z0-9_\\.\\\\-:/@#\\]","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agentbbs-tools.ts","lineNumber":74,"sourceCode":"function degradedResult(reason: string): { success: true; degraded: true; reason: string } {\n  return { success: true, degraded: true, reason };\n}\n\nfunction resolveBasePath(input?: string): string {\n  const p = input && typeof input === 'string' && input.length > 0\n    ? input\n    : '.agentbbs';\n  if (/\\.\\.[\\\\/]|\\0/.test(p)) throw new Error('basePath contains disallowed characters');\n  const abs = isAbsolute(p) ? p : resolve(getProjectCwd(), p);\n  return abs;\n}\n\nfunction validateRoomLabel(label: string): string {\n  if (!label || typeof label !== 'string') throw new Error('roomLabel is required');\n  if (label.length > 128) throw new Error('roomLabel exceeds 128 chars');\n  // Rooms are conventionally `#sales`, `#finance`, etc. — keep `#` in the allow-list.\n  if (!/^[A-Za-z0-9_.\\-:/@#]+$/.test(label)) {\n    throw new Error('roomLabel may only contain [A-Za-z0-9_.\\\\-:/@#]');\n  }\n  return label;\n}\n\nfunction validateRoomId(roomId: string): string {\n  if (!roomId || typeof roomId !== 'string') throw new Error('roomId is required');\n  if (roomId.length > 128) throw new Error('roomId exceeds 128 chars');\n  if (!/^[A-Za-z0-9_.\\-:/@#]+$/.test(roomId)) {\n    throw new Error('roomId may only contain [A-Za-z0-9_.\\\\-:/@#]');\n  }\n  return roomId;\n}\n\nfunction ensureDir(dir: string): void {\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\nfunction roomIdFromLabel(label: string): string {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/mcp-tools/agentbbs-tools.ts#L56-L92","documentation":"Thrown by validateRoomLabel() when the label is present but fails the allow-list regex `/^[A-Za-z0-9_.\\-:/@#]+$/` (label exceeds 128 chars is a separate throw). The allow-list intentionally keeps `#` (rooms are conventionally `#sales`), `:` and `/` for scoping, plus alnum and a few safe symbols — anything else (spaces, commas, unicode, shell metacharacters) is rejected because the label becomes part of a filesystem room-id hash and log lines.","triggerScenarios":"Label contains a space (`'#sales team'`), a comma, shell metacharacters (`#x;rm`), unicode characters, or punctuation not in the allow-list (`#x!`, `#x*`).","commonSituations":"Free-form user input piped straight into roomLabel; a copy-paste that introduced a trailing space or smart quote; a templating layer that joined labels with a disallowed separator.","solutions":["Restrict labels to the documented set: alphanumerics and `_ . - : / @ #`.","Sanitize/normalize the label upstream: trim whitespace, strip disallowed chars, or slugify.","If you need richer identifiers, encode them inside payload rather than the room label."],"exampleFix":"// before\nfederation_bbs_register({ roomLabel: '#sales team!' });\n// after\nfederation_bbs_register({ roomLabel: '#sales-team' });","handlingStrategy":"validation","validationCode":"const ROOM_LABEL_RE = /^[A-Za-z0-9_.\\-:/@#]+$/;\nfunction normalizeRoomLabel(label: string): string {\n  const v = label.trim();\n  if (!ROOM_LABEL_RE.test(v)) throw new Error('roomLabel has disallowed chars');\n  return v;\n}","typeGuard":"const isRoomLabel = (v: string): boolean => /^[A-Za-z0-9_.\\-:/@#]+$/.test(v);","tryCatchPattern":"null","preventionTips":["Trim and slugify free-form input before constructing room labels.","Reject smart quotes and unicode whitespace from copy-paste upstream.","Document the allow-list for your integrators so they do not guess."],"tags":["validation","agentbbs","federation","allowlist"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}