ruvnet/ruflo · error
Message not found
Error message
Message not found
What it means
Thrown by buildSubtree() when asked to materialize the message chain up to a given id but no message with that id exists in the conversation. For tree conversations the target lookup and each ancestor lookup can both fail ('Message not found' vs 'Ancestor not found'); for legacy conversations the id must appear in the flat message array.
Solutions
- Validate the id against the current conversation before building: conv.messages.some((m) => m.id === id).
- For share/permalink pages, fall back to the whole conversation or a 404 when the target was deleted.
- Refresh the conversation from the server before operating on user-supplied ids.
- Fix data integrity if ancestors reference missing nodes — the sibling 'Ancestor not found' error indicates dangling ancestor ids.
Example fix
// before
const nodes = buildSubtree(conv, messageId);
// after
if (!conv.messages.some((m) => m.id === messageId)) {
throw new Error("message no longer exists");
}
const nodes = buildSubtree(conv, messageId); Defensive patterns
Strategy: validation
Validate before calling
if (!conv.messages.some((m) => m.id === messageId)) {
throw new Error(`message ${messageId} not found in conversation`);
}
const subtree = buildSubtree(conv, messageId); Type guard
function hasMessage(conv: { messages: { id: string }[] }, id: string): boolean {
return conv.messages.some((m) => m.id === id);
} Try / catch
try {
const nodes = buildSubtree(conv, messageId);
} catch (err) {
if (String(err) === "Message not found" || String(err) === "Ancestor not found") {
return new Response("Message no longer exists", { status: 404 });
}
throw err;
} Prevention
- Validate deep-link/permalink message ids against the live conversation before rendering.
- Return 404 (not 500) for deleted share-target messages.
- Repair dangling ancestor references during data migrations to avoid the sibling 'Ancestor not found' failure.
When it happens
Trigger: buildSubtree(conv, id) with an id that is absent from conv.messages: deleted message, id from another conversation, or a client holding a stale tree snapshot after concurrent edits.
Common situations: Rendering a permalink/share link whose target message was deleted afterwards; navigating to ?message=<id> after the conversation was trimmed; concurrent sessions where one deletes messages while another builds subtrees.
Related errors
- The sibling message doesn't exist
- Cannot add a sibling to an empty conversation
- The sibling message is the root message, therefore we can't…
- You need to specify a parentId if this is not the first…
- Cannot add a sibling to a legacy conversation
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/a3e62d1ce1305f9d.
Report an issue: GitHub.
Appendix: 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 fa13ee4ad6)