{"record":{"id":"5f23b50541d140db","repo":"ruvnet/ruflo","slug":"roomid-is-required-agentbbs-federation","errorCode":null,"errorMessage":"roomId is required","messagePattern":"roomId is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts","lineNumber":317,"sourceCode":"  const next = peers.filter(p => p.nodeId !== nodeId);\n  if (next.length === peers.length) return false;\n  writePeers(basePath, next);\n  return true;\n}\n\nexport function readEnvelopes(basePath: string, roomId: string): SignedEnvelope[] {\n  const p = roomLogPath(basePath, roomId);\n  if (!existsSync(p)) return [];\n  const out: SignedEnvelope[] = [];\n  for (const line of readFileSync(p, 'utf-8').split(/\\r?\\n/)) {\n    if (!line.trim()) continue;\n    try { out.push(JSON.parse(line)); } catch { /* skip malformed */ }\n  }\n  return out;\n}\n\nexport function 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  // Also blocks path traversal: `.` is allowed but `/` segments cannot form\n  // `..` without tripping the explicit check below.\n  if (!ROOM_ID_RE.test(roomId)) throw new Error('roomId has invalid characters');\n  if (roomId.includes('..')) throw new Error('roomId must not contain ..');\n  return roomId;\n}\n\nexport interface MergeResult {\n  merged: number;\n  skippedDuplicate: number;\n  skippedUnverified: number;\n  skippedOversize: number;\n  skippedHopLimit: number;\n}\n\n/**\n * Union-merge verified envelopes from a peer into the local room log.","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/ruvnet/ruflo/blob/2602b642d92234c710ffbe96bfb33007d481ceab/v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts#L299-L335","documentation":"validateRoomId() requires a non-empty string roomId before any room-scoped federation operation proceeds. A missing, null, undefined, or empty roomId cannot identify a room on disk, so the library fails fast. This is the first of several checks (length, charset, traversal) applied to room identifiers.","triggerScenarios":"Calling mergeEnvelopes, syncRoomFromPeer, or a server handler with roomId === '' , null, or undefined — e.g. an envelope JSON parsed from a peer line lacking a roomId field, or a request handler that never extracted the room from the URL/body.","commonSituations":"Malformed envelopes from a remote peer missing the roomId field; frontend sending an empty room query param; refactored code path dropping the roomId argument; a sync job iterating records where roomId is optional.","solutions":["Check the caller and ensure a non-empty roomId is passed (default it or reject the request earlier).","Validate/skip envelopes without roomId before merging: if (!env.roomId) continue.","At API boundaries, return a 400 to clients that omit roomId instead of letting it reach the sync layer.","Add a runtime check at the entry point that produces the call so the failure surfaces where the data originates."],"exampleFix":"// before\nsyncRoomFromPeer(base, peer, envelope.roomId); // throws when missing\n// after\nif (!envelope.roomId) { log.warn('envelope missing roomId'); return; }\nsyncRoomFromPeer(base, peer, envelope.roomId);","handlingStrategy":"validation","validationCode":"function hasRoomId(v) { return typeof v === 'string' && v.length > 0; }\nif (!hasRoomId(envelope.roomId)) throw new Error('roomId is required');","typeGuard":"const hasRoomId = (v: unknown): v is string => typeof v === 'string' && v.length > 0;","tryCatchPattern":"try {\n  syncRoomFromPeer(basePath, peer, roomId);\n} catch (e) {\n  if (e.message === 'roomId is required') {\n    log.warn('skipping envelope without roomId');\n    return;\n  }\n  throw e;\n}","preventionTips":["Validate envelope shape (including roomId) at ingest before merging","Reject requests missing roomId at the API boundary with a 400","Make roomId a required, non-optional field in your envelope type/serializer","Fail fast where the data originates, not deep in the sync layer"],"tags":["validation","missing-argument","room-id","input-validation"],"backgroundTag":"missing-required-argument","analyzedSha":"2602b642d92234c710ffbe96bfb33007d481ceab","analyzedAt":"2026-09-15T22:58:14.805Z","contentChangedAt":"2026-09-15T22:58:14.805Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}