{"record":{"id":"f2c6c7947f571020","repo":"HelloZeroNet/ZeroNet","slug":"the-selector-for-the-root-vnode-may-not-be-changed-f2c6c7","errorCode":null,"errorMessage":"The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)","messagePattern":"The selector for the root VNode may not be changed\\. \\(consider using dom\\.merge and add one extra level to the virtual DOM\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/UiConfig/media/js/lib/maquette.js","lineNumber":484,"sourceCode":"                }\n            }\n            updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;\n            updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;\n            if (vnode.properties && vnode.properties.afterUpdate) {\n                vnode.properties.afterUpdate(domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children);\n            }\n        }\n        if (updated && vnode.properties && vnode.properties.updateAnimation) {\n            vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);\n        }\n        vnode.domNode = previous.domNode;\n        return textUpdated;\n    };\n    var createProjection = function (vnode, projectionOptions) {\n        return {\n            update: function (updatedVnode) {\n                if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {\n                    throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');\n                }\n                updateDom(vnode, updatedVnode, projectionOptions);\n                vnode = updatedVnode;\n            },\n            domNode: vnode.domNode\n        };\n    };\n    ;\n    // The other two parameters are not added here, because the Typescript compiler creates surrogate code for desctructuring 'children'.\n    exports.h = function (selector) {\n        var properties = arguments[1];\n        if (typeof selector !== 'string') {\n            throw new Error();\n        }\n        var childIndex = 1;\n        if (properties && !properties.hasOwnProperty('vnodeSelector') && !Array.isArray(properties) && typeof properties === 'object') {\n            childIndex = 2;\n        } else {","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/HelloZeroNet/ZeroNet/blob/454c0b2e7e000fda7000cba49027541fbf327b96/plugins/UiConfig/media/js/lib/maquette.js#L466-L502","documentation":"createProjection's update function throws when the updated root vnode has a different vnodeSelector than the one the projection was created with. maquette only diffs against the same node type at the root; changing the root selector requires a different DOM structure, so it suggests dom.merge with an extra wrapper level.","triggerScenarios":"Calling projection.update(h('div', ...)) on a projection originally created from h('span', ...), or conditionally returning a different root element type from the top-level render function.","commonSituations":"Conditionally rendering different root tags (div vs span, or component vs null placeholder) from the root render function; refactoring a component's root element while a projector holds an old projection.","solutions":["Keep the root vnode selector constant across updates.","Wrap the variable content in a stable outer element, e.g. always return h('div', [conditionalContent]).","Use dom.merge/dom.append with an extra wrapper level as the error message suggests.","Tear down and re-create the projector/projection if the root must genuinely change."],"exampleFix":"// before\nprojection.update(condition ? h('div', [x]) : h('span', [x]))\n// after\nprojection.update(h('div', [condition ? x : y]))","handlingStrategy":"validation","validationCode":"function assertSameRootSelector(prevVnode, nextVnode) {\n  if (prevVnode.vnodeSelector !== nextVnode.vnodeSelector) {\n    throw new Error('Root selector must stay constant: ' + prevVnode.vnodeSelector);\n  }\n}\n","typeGuard":"function sameRoot(prev, next) {\n  return prev.vnodeSelector === next.vnodeSelector;\n}","tryCatchPattern":"try {\n  projection.update(updatedVnode);\n} catch (e) {\n  if (/selector for the root VNode may not be changed/.test(e.message)) {\n    projection = projector.createDom(wrapInStableRoot(updatedVnode), extensions);\n  } else { throw e; }\n}","preventionTips":["Always return the same root element type from the top-level render function.","Wrap variable content in a constant outer h('div', [...]) container.","If the root must change, recreate the projection/projector instead of updating."],"tags":["virtual-dom","projection","root-selector"],"backgroundTag":"root-vnode-selector-changed","analyzedSha":"454c0b2e7e000fda7000cba49027541fbf327b96","analyzedAt":"2026-09-02T19:46:57.278Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}