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

  1. Add a unique `key` property to each repeated child: h('li', { key: item.id }, ...).
  2. If no natural id exists, add a counter-based unique key when items are created.
  3. 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

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


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