ruvnet/ruflo · error · Error

Cannot add a sibling to an empty conversation

Error message

Cannot add a sibling to an empty conversation

What it means

Thrown by addSibling at the top of the function when conv.messages.length === 0. Siblings are defined relative to an existing node's nearest ancestor, so adding one to an empty conversation is meaningless; the caller must first create a root via addChildren.

Source

Thrown at ruflo/src/ruvocal/src/lib/utils/tree/addSibling.ts:6

import { v4 } from "uuid";
import type { Tree, TreeId, NewNode, TreeNode } from "./tree";

export function addSibling<T>(conv: Tree<T>, message: NewNode<T>, siblingId: TreeId): TreeId {
	if (conv.messages.length === 0) {
		throw new Error("Cannot add a sibling to an empty conversation");
	}
	if (!conv.rootMessageId) {
		throw new Error("Cannot add a sibling to a legacy conversation");
	}

	const sibling = conv.messages.find((m) => m.id === siblingId);

	if (!sibling) {
		throw new Error("The sibling message doesn't exist");
	}

	if (!sibling.ancestors || sibling.ancestors?.length === 0) {
		throw new Error("The sibling message is the root message, therefore we can't add a sibling");
	}

	const messageId = v4();

	conv.messages.push({

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Guard the call: if conv.messages.length === 0, call addChildren(conv, message) to seed the root instead.
  2. Ensure the conversation is loaded/persisted with at least one message before exposing a "create sibling" / "retry" control.
  3. In tests, seed the tree with addChildren before exercising addSibling.

Example fix

// before
addSibling(conv, message, siblingId); // throws on empty conv
// after
if (conv.messages.length === 0) {
  addChildren(conv, message);
} else {
  addSibling(conv, message, siblingId);
}
Defensive patterns

Strategy: validation

Validate before calling

function ensureSeed<T>(conv: Tree<T>, message: NewNode<T>, siblingId?: string) {
  if (conv.messages.length === 0) return addChildren(conv, message);
  if (!siblingId) throw new Error("siblingId required for non-empty conversation");
  return addSibling(conv, message, siblingId);
}

Type guard

function isEmptyTree<T>(conv: Tree<T>): boolean {
  return conv.messages.length === 0;
}

Prevention

When it happens

Trigger: Calling addSibling on a brand-new or freshly-cleared conversation before any message has been inserted.

Common situations: A UI "regenerate" action firing on an uninitialised conversation; a unit test that constructs an empty Tree and immediately calls addSibling; a race where the conversation was reset between the message-list check and the addSibling call.

Related errors


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