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
- Re-read the conversation from the store/API before the operation and use an id from that fresh snapshot.
- Handle the deletion race: if the sibling is gone, fall back to addChildren on the current leaf or refresh the UI.
- Normalize ids (trim, exact case) — uuids are case-sensitive.
- 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
- Always derive sibling ids from a freshly loaded conversation snapshot.
- Re-fetch the conversation when handling user-supplied message ids (share links, deep links).
- Trim/normalize ids before comparison; uuids are case-sensitive.
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
- Message not found
- 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/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)