{"record":{"id":"a3e62d1ce1305f9d","repo":"ruvnet/ruflo","slug":"message-not-found","errorCode":null,"errorMessage":"Message not found","messagePattern":"Message not found","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/utils/tree/buildSubtree.ts","lineNumber":8,"sourceCode":"import type { Tree, TreeId, TreeNode } from \"./tree\";\n\nexport function buildSubtree<T>(conv: Tree<T>, id: TreeId): TreeNode<T>[] {\n\tif (!conv.rootMessageId) {\n\t\tif (conv.messages.length === 0) return [];\n\t\t// legacy conversation slice up to id\n\t\tconst index = conv.messages.findIndex((m) => m.id === id);\n\t\tif (index === -1) throw new Error(\"Message not found\");\n\t\treturn conv.messages.slice(0, index + 1);\n\t} else {\n\t\t// find the message with the right id then create the ancestor tree\n\t\tconst message = conv.messages.find((m) => m.id === id);\n\t\tif (!message) throw new Error(\"Message not found\");\n\n\t\treturn [\n\t\t\t...(message.ancestors?.map((ancestorId) => {\n\t\t\t\tconst ancestor = conv.messages.find((m) => m.id === ancestorId);\n\t\t\t\tif (!ancestor) throw new Error(\"Ancestor not found\");\n\t\t\t\treturn ancestor;\n\t\t\t}) ?? []),\n\t\t\tmessage,\n\t\t];\n\t}\n}\n","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/utils/tree/buildSubtree.ts#L1-L25","documentation":"Thrown by buildSubtree when the requested id cannot be located in conv.messages. For legacy conversations (no rootMessageId) it uses findIndex and throws if -1; for tree conversations it uses find and throws on undefined. There is a separate \"Ancestor not found\" throw when the message exists but one of its recorded ancestors is missing from the array (a corrupted/partial tree).","triggerScenarios":"Requesting the subtree for a message id that was deleted, belongs to a different conversation, or is a client placeholder not yet in messages; or a tree where an ancestor id in the chain no longer exists in the messages array.","commonSituations":"URL deep-link to a deleted message; optimistic id mismatch; a partially-loaded conversation (only a slice of messages was fetched); corruption where an ancestor was removed but descendants still reference it.","solutions":["Verify the id exists in conv.messages before calling buildSubtree.","If the id came from a URL, refetch the full conversation and re-check.","For the \"Ancestor not found\" variant, run a repair pass that prunes dangling ancestor ids or rebuilds the tree from convertLegacyConversation.","Return [] / a not-found UI rather than throwing when the id is user-supplied."],"exampleFix":"// before\nconst sub = buildSubtree(conv, id); // throws if id absent\n// after\nif (!conv.messages.some((m) => m.id === id)) return [];\nconst sub = buildSubtree(conv, id);","handlingStrategy":"validation","validationCode":"if (!conv.messages.some((m) => m.id === id)) {\n  return []; // or throw a typed NotFound error\n}\nreturn buildSubtree(conv, id);","typeGuard":"function hasMessage<T>(conv: Tree<T>, id: string): boolean {\n  return conv.messages.some((m) => m.id === id);\n}","tryCatchPattern":"try {\n  return buildSubtree(conv, id);\n} catch (e) {\n  if (String((e as Error)?.message) === \"Message not found\" || String((e as Error)?.message) === \"Ancestor not found\") {\n    return []; // graceful not-found for user-supplied ids\n  }\n  throw e;\n}","preventionTips":["Validate the id is in conv.messages before building the subtree.","For URL-deep-linked ids, refetch the full conversation first.","Run a repair pass to prune dangling ancestor ids from descendants."],"tags":["tree","conversation","validation"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}