stride3d/stride · error · InvalidOperationException

This operation can only be executed on a child of a panel.

Error message

This operation can only be executed on a child of a panel.

What it means

Thrown by UIElementViewModel.Duplicate when the element's Parent is not a PanelViewModel. Duplication clones the element's sub-hierarchy and expects to re-insert it under a panel parent; with a non-panel (e.g. the root or a non-container parent) there is no valid insertion target.

Solutions

  1. Duplicate an element that is a child of a panel; for the root, create a new root instead of duplicating.
  2. Wrap the element in a panel first (or add a panel parent), then duplicate.
  3. Guard the call: only invoke Duplicate() when vm.Parent is PanelViewModel.

Example fix

// before
rootElementViewModel.Duplicate(); // Parent is not a panel
// after
if (rootElementViewModel.Parent is PanelViewModel panel)
    panel.Children.OfType<UIElementViewModel>().First().Duplicate();
else
    // create a new root via AddChildren instead of duplicating
Defensive patterns

Strategy: type-guard

Validate before calling

bool canDuplicate = elementViewModel?.Parent is PanelViewModel;

Type guard

static bool CanDuplicate(UIElementViewModel vm) => vm?.Parent is PanelViewModel;

Try / catch

try { vm.Duplicate(); } catch (InvalidOperationException ex) { /* disable duplicate for non-panel children */ }

Prevention

When it happens

Trigger: Calling Duplicate() on a UIElementViewModel whose Parent is null or is not a PanelViewModel — e.g. duplicating the root element of a UIPage/library or an element parented to a non-panel container.

Common situations: Invoking duplicate on the single root element of a UIPage; scripting duplication on root nodes; UI states where the parent container was replaced and is no longer a Panel.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/550ccce4a682a4bf. Report an issue: GitHub.

Appendix: source

Thrown at sources/editor/Stride.Assets.Presentation/AssetEditors/UIEditor/ViewModels/UIElementViewModel.cs:144

        }

        /// <inheritdoc/>
        public override void Destroy()
        {
            nameNodeBinding.Dispose();
            propagator.Destroy();
            // Remove adorner
            Editor.Controller.AdornerService.RemoveAdorner(Id.ObjectId).Forget();
            var basePrefabNode = Editor.NodeContainer.GetNode(UIElementDesign)[nameof(UIElementDesign.Base)];
            basePrefabNode.ValueChanged -= BaseElementChanged;
            base.Destroy();
        }

        [NotNull]
        public UIElementViewModel Duplicate()
        {
            var parentPanel = Parent as PanelViewModel;
            if (parentPanel == null) throw new InvalidOperationException("This operation can only be executed on a child of a panel.");

            var subTreeRoot = AssetSideUIElement.Id;
            var flags = SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects;
            var clonedHierarchy = UIAssetPropertyGraph.CloneSubHierarchies(Asset.Session.AssetNodeContainer, Asset.Asset, subTreeRoot.Yield(), flags, out _);
            AssetPartsAnalysis.GenerateNewBaseInstanceIds(clonedHierarchy);

            var addedRoot = clonedHierarchy.Parts[clonedHierarchy.RootParts.Single().Id];
            // Inside a library, rename the element to avoid having the same name
            if (Asset is UILibraryViewModel && !string.IsNullOrEmpty(addedRoot.UIElement.Name))
                addedRoot.UIElement.Name = NamingHelper.ComputeNewName(addedRoot.UIElement.Name, Parent.Children, x => x.Name);
            Asset.AssetHierarchyPropertyGraph.AddPartToAsset(clonedHierarchy.Parts, addedRoot, parentPanel.AssetSideUIElement, parentPanel.Children.IndexOf(this) + 1);
            var cloneId = addedRoot.UIElement.Id;

            // The view model should already exist at that point
            var partId = new AbsoluteId(Asset.Id, cloneId);
            var viewModel = (UIElementViewModel)Editor.FindPartViewModel(partId);
            if (viewModel == null) throw new InvalidOperationException($"{nameof(viewModel)} can't be null.");
            return viewModel;

View on GitHub (pinned to 96fad776d2)