{"record":{"id":"fac3ddf9258ca982","repo":"ruvnet/ruflo","slug":"the-sibling-message-doesn-t-exist","errorCode":null,"errorMessage":"The sibling message doesn't exist","messagePattern":"The sibling message doesn't exist","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts","lineNumber":15,"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","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts#L1-L33","documentation":"Thrown by addSibling when conv.messages.find(m => m.id === siblingId) returns undefined. The sibling is the reference point whose ancestors the new node will share, so an unknown siblingId cannot be resolved.","triggerScenarios":"Passing a stale siblingId (deleted message, id from a different conversation, a client-generated id that was never persisted), or a typo in the id.","commonSituations":"Optimistic UI that creates a local id then calls addSibling before the server-issued id is known; the sibling message was deleted in another tab; a copy-paste of an id with trailing whitespace.","solutions":["Look up the siblingId in conv.messages before calling addSibling; if absent, refetch the conversation.","Ensure the id you pass is the server-persisted id, not a client placeholder.","Trim/normalise id strings before lookup."],"exampleFix":"// before\naddSibling(conv, message, siblingId);\n// after\nif (!conv.messages.some((m) => m.id === siblingId)) {\n  throw new Error(`sibling ${siblingId} not in conversation ${conv.id ?? \"?\"}`);\n}\naddSibling(conv, message, siblingId);","handlingStrategy":"validation","validationCode":"const sibling = conv.messages.find((m) => m.id === siblingId);\nif (!sibling) {\n  throw new Error(`sibling ${siblingId} not found in conversation`);\n}\naddSibling(conv, message, siblingId);","typeGuard":"function hasMessage<T>(conv: Tree<T>, id: string): boolean {\n  return conv.messages.some((m) => m.id === id);\n}","tryCatchPattern":null,"preventionTips":["Refetch the conversation if the siblingId came from a stale UI state.","Use server-persisted ids, not client placeholders, as sibling references.","Trim whitespace from ids before lookup."],"tags":["tree","conversation","validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}