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

  1. Add a unique `key` property to every child in repeated lists.
  2. Ensure keys persist across renders so maquette can match removed entries.
  3. 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

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


AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02). Data as JSON: /api/errors/d878028ef5115815. Report an issue: GitHub.