{"record":{"id":"2365455242df9de9","repo":"ruvnet/ruflo","slug":"cannot-add-a-sibling-to-an-empty-conversation","errorCode":null,"errorMessage":"Cannot add a sibling to an empty conversation","messagePattern":"Cannot add a sibling to an empty conversation","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts","lineNumber":6,"sourceCode":"import { v4 } from \"uuid\";\nimport type { Tree, TreeId, NewNode, TreeNode } from \"./tree\";\n\nexport function addSibling<T>(conv: Tree<T>, message: NewNode<T>, siblingId: TreeId): TreeId {\n\tif (conv.messages.length === 0) {\n\t\tthrow new Error(\"Cannot add a sibling to an empty conversation\");\n\t}\n\tif (!conv.rootMessageId) {\n\t\tthrow new Error(\"Cannot add a sibling to a legacy conversation\");\n\t}\n\n\tconst sibling = conv.messages.find((m) => m.id === siblingId);\n\n\tif (!sibling) {\n\t\tthrow new Error(\"The sibling message doesn't exist\");\n\t}\n\n\tif (!sibling.ancestors || sibling.ancestors?.length === 0) {\n\t\tthrow new Error(\"The sibling message is the root message, therefore we can't add a sibling\");\n\t}\n\n\tconst messageId = v4();\n\n\tconv.messages.push({","sourceCodeStart":1,"sourceCodeEnd":24,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts#L1-L24","documentation":"Thrown by addSibling at the top of the function when conv.messages.length === 0. Siblings are defined relative to an existing node's nearest ancestor, so adding one to an empty conversation is meaningless; the caller must first create a root via addChildren.","triggerScenarios":"Calling addSibling on a brand-new or freshly-cleared conversation before any message has been inserted.","commonSituations":"A UI \"regenerate\" action firing on an uninitialised conversation; a unit test that constructs an empty Tree and immediately calls addSibling; a race where the conversation was reset between the message-list check and the addSibling call.","solutions":["Guard the call: if conv.messages.length === 0, call addChildren(conv, message) to seed the root instead.","Ensure the conversation is loaded/persisted with at least one message before exposing a \"create sibling\" / \"retry\" control.","In tests, seed the tree with addChildren before exercising addSibling."],"exampleFix":"// before\naddSibling(conv, message, siblingId); // throws on empty conv\n// after\nif (conv.messages.length === 0) {\n  addChildren(conv, message);\n} else {\n  addSibling(conv, message, siblingId);\n}","handlingStrategy":"validation","validationCode":"function ensureSeed<T>(conv: Tree<T>, message: NewNode<T>, siblingId?: string) {\n  if (conv.messages.length === 0) return addChildren(conv, message);\n  if (!siblingId) throw new Error(\"siblingId required for non-empty conversation\");\n  return addSibling(conv, message, siblingId);\n}","typeGuard":"function isEmptyTree<T>(conv: Tree<T>): boolean {\n  return conv.messages.length === 0;\n}","tryCatchPattern":null,"preventionTips":["Guard addSibling with a messages.length check and seed via addChildren when empty.","Disable the UI 'retry/sibling' control until at least one message exists.","In tests, always seed the tree before exercising addSibling."],"tags":["tree","conversation","validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}