dotnet/wpf · error · ArgumentException

' ' is not a Visual or Visual3D.

Error message

'{0}' is not a Visual or Visual3D.

What it means

VisualTreeUtils.EnsureVisual throws ArgumentException ('{0}' is not a Visual or Visual3D.) when the supplied DependencyObject is neither a Visual nor a Visual3D, and then calls VerifyAccess to enforce thread affinity. The library requires all elements it manipulates to derive from the visual layer, not merely from DispatcherObject/DependencyObject.

Solutions

  1. Cast to Visual or Visual3D first and reject anything that fails the cast
  2. Only pass elements that live in a visual tree (controls, panels, shapes)
  3. Ensure the call runs on the object's owning UI thread

Example fix

// before
VisualTreeUtils.EnsureNonNullVisual((DependencyObject)obj);
// after
if (obj is Visual || obj is Visual3D)
{
    VisualTreeUtils.EnsureNonNullVisual((DependencyObject)obj);
}
Defensive patterns

Strategy: type-guard

Validate before calling

bool isVisual = element is Visual || element is Visual3D;

Type guard

static bool IsVisualElement(DependencyObject d) => d is Visual || d is Visual3D;

Try / catch

try { VisualTreeUtils.EnsureNonNullVisual(element); }
catch (ArgumentException ex) when (ex.Message.Contains("not a Visual")) { /* handle non-visual element */ }

Prevention

When it happens

Trigger: Passing a DependencyObject that is not a Visual — e.g. a freezable, a ResourceDictionary, a Binding expression, or a non-visual DependencyObject subclass — to an API guarded by EnsureNonNullVisual; also calling from a background thread (the subsequent VerifyAccess fails).

Common situations: Retrieving objects from Resources or code-behind fields typed as DependencyObject and assuming they are controls; passing Application-level or model objects; cross-thread access from a worker thread.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/f20ab7b003600529. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Media/VisualTreeUtils.cs:219

        ///     Throws if the given element is not a Visual or Visual3D
        ///     or if the Visual is on the wrong thread.
        /// </summary>
        private static void EnsureVisual(DependencyObject element, bool allowNull)
        {
            if (element == null)
            {
                if (!allowNull)
                {
                    throw new ArgumentNullException(nameof(element));
                }

                return;
            }

            // Would DTypes be faster?
            if (!(element is Visual || element is Visual3D))
            {
                throw new ArgumentException(SR.Visual_NotAVisual);
            }

            element.VerifyAccess();            
        }

        /// <summary>
        ///     Throws if the given element is null or not a visual type, otherwise
        ///     either visual or visual3D will be non-null on exit.
        /// </summary>
        internal static void AsNonNullVisual(DependencyObject element, out Visual visual, out Visual3D visual3D)
        {
            ArgumentNullException.ThrowIfNull(element);

            AsVisual(element, out visual, out visual3D);

            Debug.Assert((visual == null) != (visual3D == null),
                "Either visual or visual3D exclusively should be non-null.");
        }

View on GitHub (pinned to 81131a70a4)