stride3d/stride · error · InvalidOperationException

The control corresponding to the given parentId is a…

Error message

The control corresponding to the given parentId is a ContentControl that already has a Content.

What it means

UIEditorController.AddPart throws this InvalidOperationException when the target parent is a ContentControl that already has Content. A ContentControl holds exactly one child; assigning a second would silently overwrite existing game-side content, so the controller refuses the add.

Solutions

  1. Remove or move the existing content of the ContentControl before adding the new part.
  2. Use a Panel-based parent (StackPanel, Grid) that accepts multiple children.
  3. Check the parent's content (ContentControl.Content != null) before calling AddPart.
  4. If replacement is intended, update the existing content node instead of adding a new part.

Example fix

// before
controller.AddPart(newPart, contentControlId); // throws if content exists
// after
var parent = FindViewModel(contentControlId) as ContentControlViewModel;
if (parent?.Content == null)
    controller.AddPart(newPart, contentControlId);
else
    controller.AddPart(newPart, panelParentId);
Defensive patterns

Strategy: type-guard

Validate before calling

var parentVm = FindViewModel(parentId) as ContentControlViewModel;
bool canAdd = parentVm != null && parentVm.Content == null;

Type guard

bool CanHostContent(UIElementViewModel parent) => parent is ContentControlViewModel cc && cc.Content == null;

Try / catch

try { controller.AddPart(newPart, parentId); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already has a Content")) {
    // pick a panel parent or clear the existing content first
}

Prevention

When it happens

Trigger: Adding a part whose parentId refers to a ContentControl that already contains a child; calling AddPart twice with the same single-child parent; dragging multiple elements into a single-slot container.

Common situations: Duplicating elements into a button/content host that already has content, replaying scripts against an already-populated layout, drag-drop of several items onto one ContentControl.

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/58401c1083d65c43. Report an issue: GitHub.

Appendix: source

Thrown at sources/editor/Stride.Assets.Presentation/AssetEditors/UIEditor/Services/UIEditorController.cs:237

                    RootElements[assetSidePart.Id] = gameSidePart;
                }
                else
                {
                    var parentElement = (UIElement)FindPart(parentId.Value);
                    if (parentElement == null)
                        throw new InvalidOperationException($"The given {nameof(parentId)} does not correspond to any existing part.");

                    var panel = parentElement as Panel;
                    var contentControl = parentElement as ContentControl;
                    if (panel != null)
                    {
                        GameSideNodeContainer.GetNode(panel.Children).Add(gameSidePart);
                    }
                    else if (contentControl != null)
                    {
                        if (contentControl.Content != null)
                        {
                            throw new InvalidOperationException($"The control corresponding to the given {nameof(parentId)} is a ContentControl that already has a Content.");
                        }
                        GameSideNodeContainer.GetNode(contentControl)[nameof(contentControl.Content)].Update(gameSidePart);
                    }
                }
            });
        }

        /// <inheritdoc />
        public override Task RemovePart([NotNull] UIHierarchyItemViewModel parent, UIElement assetSidePart)
        {
            EnsureAssetAccess();

            return InvokeAsync(() =>
            {
                Logger.Debug($"Removing element {assetSidePart.Id} from game-side scene");
                var partId = new AbsoluteId(AssetId.Empty, assetSidePart.Id);
                var part = (UIElement)FindPart(partId);
                if (part == null)

View on GitHub (pinned to 96fad776d2)