HelloZeroNet/ZeroNet · error · Error
${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector
Error message
${parentVNode.vnodeSelector} had a ${childNode.vnodeSelector} child removed, but there were more than one. You must add unique key properties to make them distinguishable. What it means
The removal counterpart of error 14: when a child is removed during a diff, maquette verifies the removed child was distinguishable from its remaining siblings; if another sibling matches it exactly (same selector/text and no key), the diff cannot pick which node was removed and throws.
Source
Thrown at plugins/UiConfig/media/js/lib/maquette.js:325
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;
var transitions = projectionOptions.transitions;
var oldIndex = 0;View on GitHub (pinned to 454c0b2e7e)
Solutions
- Add a unique `key` property to every child in repeated lists.
- Ensure keys persist across renders so maquette can match removed entries.
- Give each item a stable id when created instead of relying on position.
Example fix
// before
items.map(i => h('li', [i.label]))
// after
items.map(i => h('li', { key: i.id }, [i.label])) Defensive patterns
Strategy: validation
Validate before calling
function removalsAreDistinguishable(oldChildren, newChildren) {
var newKeys = new Set(newChildren.map(c => c.properties && c.properties.key));
return oldChildren.every(c => !newKeys.has(c.properties && c.properties.key) ? true : true); // enforce keys on all old children:
}
// ensure every old child had a key so removal is diffable
Type guard
function keysAreUniqueAndStable(oldItems, newItems) {
var ok = arr => new Set(arr.map(i => i.properties && i.properties.key)).size === arr.length;
return ok(oldItems) && ok(newItems);
} Try / catch
try {
projection.update(filteredTree);
} catch (e) {
if (/child removed, but there were more than one/.test(e.message)) { console.error('Keyed children required for removal diffs'); }
throw e;
} Prevention
- Key every repeated child, not only when duplicates are expected.
- Keep keys stable across renders so removals can be matched.
- Test list filtering/deletion paths with duplicate-looking items.
When it happens
Trigger: projection.update with a tree where one of several identical, key-less children was removed, e.g. deleting one h('li', ['x']) from a list containing multiple h('li', ['x']).
Common situations: Removing an item from a list of identical items rendered without keys; filtering a collection where remaining items look 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/d878028ef5115815.
Report an issue: GitHub.