{"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":"exception","errorClass":null,"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/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts#L1-L33","documentation":"Thrown by addSibling() when no message with the given siblingId exists in conv.messages. The lookup is a linear find by id; a miss means the caller referenced a deleted message, a message from another conversation, or a stale id from an outdated client copy of the tree.","triggerScenarios":"addSibling(conv, msg, id) where id is not in conv.messages: concurrent deletion removed the sibling; multiple tabs holding different snapshots of the same conversation; copy-paste of an id with whitespace or case differences.","commonSituations":"Branch regeneration racing with message deletion in another tab; client-side state desync after a page restore from an old snapshot; tests using fabricated uuids that were never inserted.","solutions":["Re-read the conversation from the store/API before the operation and use an id from that fresh snapshot.","Handle the deletion race: if the sibling is gone, fall back to addChildren on the current leaf or refresh the UI.","Normalize ids (trim, exact case) — uuids are case-sensitive.","In multi-tab apps, revalidate against the server copy before mutating the tree."],"exampleFix":"// before\naddSibling(conv, message, siblingId);\n\n// after\nconst sibling = conv.messages.find((m) => m.id === siblingId);\nif (!sibling) {\n\tthrow new Error(`sibling ${siblingId} no longer exists; reload conversation`);\n}\naddSibling(conv, message, siblingId);","handlingStrategy":"validation","validationCode":"if (!conv.messages.some((m) => m.id === siblingId)) {\n\tthrow new Error(`sibling ${siblingId} not in conversation`);\n}\naddSibling(conv, message, siblingId);","typeGuard":"function messageExists(conv: { messages: { id: string }[] }, id: string): boolean {\n\treturn conv.messages.some((m) => m.id === id);\n}","tryCatchPattern":"try {\n\taddSibling(conv, message, siblingId);\n} catch (err) {\n\tif (String(err).includes(\"doesn't exist\")) {\n\t\tawait reloadConversation(conv.id); // resync then retry once\n\t} else throw err;\n}","preventionTips":["Always derive sibling ids from a freshly loaded conversation snapshot.","Re-fetch the conversation when handling user-supplied message ids (share links, deep links).","Trim/normalize ids before comparison; uuids are case-sensitive."],"tags":["tree","conversation","stale-state","validation"],"backgroundTag":"invalid-tree-operation","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}