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
When children without key properties are added, maquette tries to find the new child by comparing it against existing sibling vnodes. If more than one existing child matches (same vnodeSelector and same key — i.e. both keyless and identical selectors), maquette cannot determine which position the child belongs at and throws instead of guessing.
Source
Thrown at plugins/UiConfig/media/js/all.js:512
}
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 child in the list: h('li', { key: item.id }, ...).
- Ensure keys are stable per item (database id), not array index, so additions/removals reconcile correctly.
- If items are genuinely indistinguishable, append new children at the end only, or restructure so each child has a unique selector.
Example fix
// before
items.map(function (item) { return h('li', [item.text]); })
// after
items.map(function (item) { return h('li', { key: item.id }, [item.text]); }) Defensive patterns
Strategy: validation
Validate before calling
function assertKeyedChildren(vnode) {
var children = (vnode.children || []).filter(function (c) { return c && c.vnodeSelector; });
var selectors = {};
children.forEach(function (c) {
var key = c.properties && c.properties.key;
if (!key) {
selectors[c.vnodeSelector] = (selectors[c.vnodeSelector] || 0) + 1;
}
});
Object.keys(selectors).forEach(function (s) {
if (selectors[s] > 1) throw new Error('Duplicate keyless children with selector ' + s + '; add key properties');
});
} Type guard
function childrenAreKeyed(vnode) {
return (vnode.children || []).every(function (c) {
return !c || !c.vnodeSelector || (c.properties && c.properties.key != null);
});
} Try / catch
try {
projection.update(updatedVnode);
} catch (e) {
if (e.message.indexOf('unique key properties') !== -1) {
console.error('Add stable key properties to list children: ' + e.message);
}
throw e;
} Prevention
- Give every child in a dynamic list a unique, stable key (item.id).
- Never use array index as key for mutable/insertable lists.
- Treat any repeated sibling vnodeSelector without keys as a bug.
- Write render tests that add/remove list items to exercise reconciliation.
When it happens
Trigger: Calling projection.update (or a projector render) where a new keyless child is inserted into a parent that already contains two or more other keyless children with the identical vnodeSelector (e.g. two h('li', ...) items, then a third is added).
Common situations: Rendering dynamic lists (todo items, table rows, menu entries) without key properties; lists where all items render the same tag/class; inserting items at the top/middle of a list rather than appending.
Related errors
- ${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
- Provide a transitions object to the projectionOptions to do
- Style values must be strings
- Property "className" is not supported, use "class".
- "class" property may not be updated. Use the "classes" prope
AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02).
Data as JSON: /api/errors/1b43082ed79e35aa.
Report an issue: GitHub.