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

  1. Verify the id exists in conv.messages before calling buildSubtree.
  2. If the id came from a URL, refetch the full conversation and re-check.
  3. For the "Ancestor not found" variant, run a repair pass that prunes dangling ancestor ids or rebuilds the tree from convertLegacyConversation.
  4. 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

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


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/a3e62d1ce1305f9d. Report an issue: GitHub.