ruvnet/ruflo · error

The sibling message doesn't exist

Error message

The sibling message doesn't exist

What it means

Thrown by addSibling() when no message with the given siblingId exists in conv.messages. The lookup is a linear find by id; a miss means the caller referenced a deleted message, a message from another conversation, or a stale id from an outdated client copy of the tree.

Solutions

  1. Re-read the conversation from the store/API before the operation and use an id from that fresh snapshot.
  2. Handle the deletion race: if the sibling is gone, fall back to addChildren on the current leaf or refresh the UI.
  3. Normalize ids (trim, exact case) — uuids are case-sensitive.
  4. In multi-tab apps, revalidate against the server copy before mutating the tree.

Example fix

// before
addSibling(conv, message, siblingId);

// after
const sibling = conv.messages.find((m) => m.id === siblingId);
if (!sibling) {
	throw new Error(`sibling ${siblingId} no longer exists; reload conversation`);
}
addSibling(conv, message, siblingId);
Defensive patterns

Strategy: validation

Validate before calling

if (!conv.messages.some((m) => m.id === siblingId)) {
	throw new Error(`sibling ${siblingId} not in conversation`);
}
addSibling(conv, message, siblingId);

Type guard

function messageExists(conv: { messages: { id: string }[] }, id: string): boolean {
	return conv.messages.some((m) => m.id === id);
}

Try / catch

try {
	addSibling(conv, message, siblingId);
} catch (err) {
	if (String(err).includes("doesn't exist")) {
		await reloadConversation(conv.id); // resync then retry once
	} else throw err;
}

Prevention

When it happens

Trigger: addSibling(conv, msg, id) where id is not in conv.messages: concurrent deletion removed the sibling; multiple tabs holding different snapshots of the same conversation; copy-paste of an id with whitespace or case differences.

Common situations: Branch regeneration racing with message deletion in another tab; client-side state desync after a page restore from an old snapshot; tests using fabricated uuids that were never inserted.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/fac3ddf9258ca982. Report an issue: GitHub.

Appendix: source

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

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({
		...message,
		id: messageId,
		ancestors: sibling.ancestors,
		children: [],
	} as TreeNode<T>);

	const nearestAncestorId = sibling.ancestors[sibling.ancestors.length - 1];
	const nearestAncestor = conv.messages.find((m) => m.id === nearestAncestorId);

View on GitHub (pinned to fa13ee4ad6)