{"record":{"id":"4d1746dc0fd6c96d","repo":"ruvnet/ruflo","slug":"the-sibling-message-is-the-root-message-therefore","errorCode":null,"errorMessage":"The sibling message is the root message, therefore we can't add a sibling","messagePattern":"The sibling message is the root message, therefore we can't add a sibling","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts","lineNumber":19,"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({\n\t\t...message,\n\t\tid: messageId,\n\t\tancestors: sibling.ancestors,\n\t\tchildren: [],\n\t} as TreeNode<T>);\n\n\tconst nearestAncestorId = sibling.ancestors[sibling.ancestors.length - 1];\n\tconst nearestAncestor = conv.messages.find((m) => m.id === nearestAncestorId);\n\n\tif (nearestAncestor) {\n\t\tif (nearestAncestor.children) {\n\t\t\tnearestAncestor.children.push(messageId);\n\t\t} else nearestAncestor.children = [messageId];","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts#L1-L37","documentation":"Thrown by addSibling when the sibling exists but has no ancestors (ancestors is undefined/empty). A node with no ancestors is the root of the tree; the root has no parent to attach a sibling under, so adding one is structurally impossible.","triggerScenarios":"Passing the conversation's rootMessageId (or any node whose ancestors chain is []) as siblingId — e.g. a \"regenerate\" action triggered on the very first user/root message.","commonSituations":"UI that offers \"try again\" on the root message; misidentifying the root id; a tree where ancestors were not populated correctly so an inner node looks like the root.","solutions":["Pick a non-root node as the sibling (any node with ancestors.length > 0).","To create alternative first turns, start a new conversation rather than adding a sibling to the root.","Validate sibling.ancestors?.length > 0 before calling addSibling."],"exampleFix":"// before\naddSibling(conv, message, conv.rootMessageId); // root → throws\n// after\nconst sibling = conv.messages.find((m) => m.id === siblingId);\nif (!sibling || !sibling.ancestors?.length) {\n  throw new Error(\"cannot branch from root; create a new conversation instead\");\n}\naddSibling(conv, message, siblingId);","handlingStrategy":"validation","validationCode":"const sibling = conv.messages.find((m) => m.id === siblingId);\nif (!sibling || !sibling.ancestors || sibling.ancestors.length === 0) {\n  throw new Error(\"cannot add sibling to root message; start a new conversation instead\");\n}\naddSibling(conv, message, siblingId);","typeGuard":"function isRootNode<T>(node: TreeNode<T>): boolean {\n  return !node.ancestors || node.ancestors.length === 0;\n}","tryCatchPattern":null,"preventionTips":["Disable the 'branch/regenerate' affordance on the conversation root.","Validate sibling.ancestors?.length > 0 before calling addSibling.","For alternative first turns, create a new conversation rather than branching the root."],"tags":["tree","conversation","validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}