ruvnet/ruflo · error · Error
Message not found
Error message
Message not found
What it means
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).
Source
Thrown at ruflo/src/ruvocal/src/lib/utils/tree/buildSubtree.ts:8
import type { Tree, TreeId, TreeNode } from "./tree";
export function buildSubtree<T>(conv: Tree<T>, id: TreeId): TreeNode<T>[] {
if (!conv.rootMessageId) {
if (conv.messages.length === 0) return [];
// legacy conversation slice up to id
const index = conv.messages.findIndex((m) => m.id === id);
if (index === -1) throw new Error("Message not found");
return conv.messages.slice(0, index + 1);
} else {
// find the message with the right id then create the ancestor tree
const message = conv.messages.find((m) => m.id === id);
if (!message) throw new Error("Message not found");
return [
...(message.ancestors?.map((ancestorId) => {
const ancestor = conv.messages.find((m) => m.id === ancestorId);
if (!ancestor) throw new Error("Ancestor not found");
return ancestor;
}) ?? []),
message,
];
}
}
View on GitHub (pinned to 6b01dc5a68)
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.
Example fix
// before const sub = buildSubtree(conv, id); // throws if id absent // after if (!conv.messages.some((m) => m.id === id)) return []; const sub = buildSubtree(conv, id);
Defensive patterns
Strategy: validation
Validate before calling
if (!conv.messages.some((m) => m.id === id)) {
return []; // or throw a typed NotFound error
}
return buildSubtree(conv, id); Type guard
function hasMessage<T>(conv: Tree<T>, id: string): boolean {
return conv.messages.some((m) => m.id === id);
} Try / catch
try {
return buildSubtree(conv, id);
} catch (e) {
if (String((e as Error)?.message) === "Message not found" || String((e as Error)?.message) === "Ancestor not found") {
return []; // graceful not-found for user-supplied ids
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- You need to specify a parentId if this is not the first mess
- Cannot add a sibling to an empty conversation
- The sibling message doesn't exist
- The sibling message is the root message, therefore we can't
- This is a legacy conversation, you can only append to the la
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/a3e62d1ce1305f9d.
Report an issue: GitHub.