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
- Detach the visual from its current parent (remove it from its parent's Children/Child collection) before assigning it as RootVisual.
- Create a new visual instance for each CompositionTarget instead of sharing one visual tree.
- 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
- One visual tree per CompositionTarget — never share a tree between render targets.
- Detach a visual from its parent before re-hosting it.
- Set RootVisual only once per target lifecycle; reset to null before swapping.
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
- ' ' is not a Visual or Visual3D.
- 0x80004005
- inverse ? SR.Visual_NotADescendant : SR.Visual_NotAnAncestor
- Page can have only Window or Frame as parent.
- Specified index is out of range or child at index is null…
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)