dotnet/wpf · error · ArgumentException

SR.CompositionTarget_RootVisual_HasParent

Error message

SR.CompositionTarget_RootVisual_HasParent

What it means

CompositionTarget.SetRootVisual throws ArgumentException because the visual passed to RootVisual already has a parent (or is already marked as a root element). WPF cannot allow two CompositionTargets to render the same visual tree, so the visual must be parentless when it becomes a composition root.

Solutions

  1. Detach the visual from its current parent (remove it from its parent's Children/Child collection) before assigning it as RootVisual.
  2. Create a new visual instance for each CompositionTarget instead of sharing one visual tree.
  3. Check visual.Parent (or VisualTreeHelper.GetParent) is null before setting RootVisual.

Example fix

// before
compositionTarget.RootVisual = existingCanvas; // canvas already has a parent
// after
((Panel)existingCanvas.Parent).Children.Remove(existingCanvas);
compositionTarget.RootVisual = existingCanvas;
Defensive patterns

Strategy: validation

Validate before calling

if (visual != null && (VisualTreeHelper.GetParent(visual) != null)) throw new InvalidOperationException("Visual already has a parent; detach it before setting RootVisual.");
compositionTarget.RootVisual = visual;

Type guard

static bool CanBeRootVisual(Visual v) => v != null && VisualTreeHelper.GetParent(v) == null;

Prevention

When it happens

Trigger: Setting CompositionTarget.RootVisual (in MediaContext/RenderMessageHandler context) with a Visual whose Visual._parent != null or whose IsRootElement is already true — e.g. reusing a visual already attached to another HwndSource, another CompositionTarget, or an element already added to a visual tree.

Common situations: Re-hosting a control that is already in a Window's tree into a second HwndSource/CompositionTarget; calling RootVisual twice with overlapping trees; moving a visual between two render targets without detaching it first.

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 dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/380da9be27fa4b13. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/CompositionTarget.cs:461

        /// <summary>
        /// Internal method to set the root visual.
        /// </summary>
        /// <param name="visual">Root visual, can be null, but can not be a child of another
        /// Visual.</param>
        private void SetRootVisual(Visual visual)
        {
            // We need to make this function robust by leaving the
            // _rootVisual in a consistent state.

            if (visual != null &&
                (visual._parent != null
                 || visual.IsRootElement))
            {
                // If a Visual has already a parent it can not be the root in a CompositionTarget because
                // otherwise we would have two CompositionTargets party on the same Visual tree.
                // If want to allow this we need to explicitly add support for this.
                throw new System.ArgumentException(SR.CompositionTarget_RootVisual_HasParent);
            }

            DUCE.ChannelSet channelSet = MediaContext.From(Dispatcher).GetChannels();
            DUCE.Channel channel = channelSet.Channel;
            if (_rootVisual != null && _contentRoot.IsOnChannel(channel))
            {
                ClearRootNode(channel);

                ((DUCE.IResource)_rootVisual).ReleaseOnChannel(channel);

                _rootVisual.IsRootElement = false;
            }

            _rootVisual = visual;

            if (_rootVisual != null)
            {
                _rootVisual.IsRootElement = true;

View on GitHub (pinned to 81131a70a4)