{"record":{"id":"4b1fc3c4842ca439","repo":"ruvnet/ruflo","slug":"roomid-may-only-contain-a-za-z0-9","errorCode":null,"errorMessage":"roomId may only contain [A-Za-z0-9_.\\-:/@#]","messagePattern":"roomId 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":83,"sourceCode":"  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 {\n  // Stable, deterministic roomId — strip leading `#`, lowercase, and append a\n  // short hash so we don't collide across two rooms with the same canonical\n  // label but different policies. Phase 1: deterministic over (label).\n  const norm = label.replace(/^#/, '').toLowerCase();\n  const h = createHash('sha256').update(`agentbbs:room:${norm}`).digest('hex').slice(0, 8);\n  return `${norm}-${h}`;\n}\n\nfunction roomLogPath(basePath: string, roomId: string): string {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/mcp-tools/agentbbs-tools.ts#L65-L101","documentation":"Thrown by validateRoomId() when roomId is present but fails the allow-list `/^[A-Za-z0-9_.\\-:/@#]+$/` (length>128 is a separate throw). The allow-list matches validateRoomLabel so labels and ids accept the same safe symbol set; since roomId derives from a deterministic hash it should normally already be compliant, so hitting this usually means a caller hand-rolled a roomId instead of using register's output.","triggerScenarios":"Passing a hand-constructed roomId containing a space, comma, or shell metacharacter; passing the roomLabel as roomId when the label itself contained a disallowed char; unicode/whitespace from copy-paste.","commonSituations":"Integrator built roomId from user input instead of from the register result; fixture with a placeholder like 'TODO'; a templating bug that injected whitespace.","solutions":["Use the roomId returned by federation_bbs_register verbatim — it is always compliant.","If you must construct one, restrict to alnum + `_ . - : / @ #` and trim whitespace first.","Add a unit test that asserts every publish in your flow uses the register-derived id."],"exampleFix":"// before — hand-rolled id\nfederation_bbs_publish({ roomId: 'sales room 1', msgType, payload });\n// after\nfederation_bbs_publish({ roomId: reg.roomId, msgType, payload });","handlingStrategy":"validation","validationCode":"const ROOM_ID_RE = /^[A-Za-z0-9_.\\-:/@#]+$/;\nfunction requireValidRoomId(id: string): string {\n  if (!ROOM_ID_RE.test(id)) throw new Error('roomId has disallowed chars');\n  return id;\n}","typeGuard":"const isRoomId = (v: string): boolean => /^[A-Za-z0-9_.\\-:/@#]+$/.test(v) && v.length <= 128;","tryCatchPattern":"null","preventionTips":["Use register's output verbatim rather than constructing roomId yourself.","If you must construct, mirror the allow-list and length cap exactly.","Add a property test that random roomId outputs from register always validate."],"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"}