HelloZeroNet/ZeroNet · error · Error
${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
Error message
${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector} child added, but there is now more than one. You must add unique key properties to make them distinguishable. What it means
checkDistinguishable runs when a child is added during a re-render; if the new child is indistinguishable from another existing child of the same parent (no unique key), maquette cannot know which node to update and throws. Keys give each sibling a stable identity for the diff.
Source
Thrown at plugins/UiConfig/media/js/lib/maquette.js:323
}
if (domNode.parentNode) {
domNode.parentNode.removeChild(domNode);
}
};
var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
var childNode = childNodes[indexToCheck];
if (childNode.vnodeSelector === '') {
return; // Text nodes need not be distinguishable
}
var properties = childNode.properties;
var key = properties ? properties.key === undefined ? properties.bind : properties.key : undefined;
if (!key) {
for (var i = 0; i < childNodes.length; i++) {
if (i !== indexToCheck) {
var node = childNodes[i];
if (same(node, childNode)) {
if (operation === 'added') {
throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'added, but there is now more than one. You must add unique key properties to make them distinguishable.');
} else {
throw new Error(parentVNode.vnodeSelector + ' had a ' + childNode.vnodeSelector + ' child ' + 'removed, but there were more than one. You must add unique key properties to make them distinguishable.');
}
}
}
}
}
};
var createDom;
var updateDom;
var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
if (oldChildren === newChildren) {
return false;
}
oldChildren = oldChildren || emptyArray;
newChildren = newChildren || emptyArray;
var oldChildrenLength = oldChildren.length;
var newChildrenLength = newChildren.length;View on GitHub (pinned to 454c0b2e7e)
Solutions
- Add a unique `key` property to each repeated child: h('li', { key: item.id }, ...).
- If no natural id exists, add a counter-based unique key when items are created.
- Include distinguishing content (e.g. index) so siblings differ, though keys are the proper fix.
Example fix
// before
items.map(i => h('li', [i.name]))
// after
items.map(i => h('li', { key: i.id }, [i.name])) Defensive patterns
Strategy: validation
Validate before calling
function childrenHaveKeys(vnode) {
return (vnode.children || []).every(function (c) { return c == null || c.properties == null || c.properties.key != null || !isRepeatedSibling(c, vnode.children); });
}
Type guard
function allListItemsKeyed(items) {
return items.every(function (i) { return i.properties && typeof i.properties.key !== 'undefined'; });
} Try / catch
try {
projection.update(newTree);
} catch (e) {
if (/You must add unique key properties/.test(e.message)) { console.error('Add key properties to repeated children:', e.message); }
throw e;
} Prevention
- Always add key: item.id to children rendered from collections.
- Generate unique keys at item creation time (counter or uuid).
- Never rely on item text or selector to distinguish siblings.
When it happens
Trigger: Calling projection.update with a new vnode tree that adds a child identical (same selector, text, no key) to an existing sibling of the same parent, e.g. adding a second h('li', ['item']) to a list.
Common situations: Dynamically appending repeated list items (tasks, rows, comments) without keys; rendering placeholder items that are all identical.
Related errors
- ${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
- ${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
- ${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
- Provide a transitions object to the projectionOptions to do
- Style values must be strings
AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02).
Data as JSON: /api/errors/75c2e858aa0cfac7.
Report an issue: GitHub.