AvaloniaUI/Avalonia · error · InvalidOperationException

Expected control ' ' to be ' but it was ' '.

Error message

Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.

What it means

Thrown by NameScopeExtensions.Find<T>(INameScope, string) when a control with the requested name exists in the scope but is not assignable to the requested generic type T. Find<T> returns null when nothing is found, but treats a type mismatch as a programming error.

Solutions

  1. Align the generic type parameter with the actual registered control type in XAML.
  2. If you are unsure of the type, use the non-generic Find(name) and check/cast manually.
  3. If the control type genuinely varies, use Find<object> or Find<IControl> and narrow at runtime.

Example fix

// before
var box = nameScope.Find<TextBox>("myLabel"); // throws, it's a Label
// after
var label = nameScope.Find<Label>("myLabel");
Defensive patterns

Strategy: type-guard

Validate before calling

var found = nameScope.Find(name);
if (found is T typed) return typed;
return null; // or handle the type mismatch explicitly

Type guard

static T? SafeFind<T>(INameScope scope, string name) where T : class
{
    var found = scope.Find(name);
    return found as T; // null if wrong type, no throw
}

Try / catch

try { return nameScope.Find<T>(name); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Expected control"))
{ return null; }

Prevention

When it happens

Trigger: Calling nameScope.Find<TextBox>("myButton") where "myButton" is registered as a Button. The name resolves but result is T fails.

Common situations: XAML x:Name was changed to a different control type but the C# Find<T> call was not updated. Refactoring a view swapped a control type (e.g., TextBlock to Label) while Find<T> still requests the old type. Generic type inference picks the wrong T.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/97b73b0f8138a9a2. Report an issue: GitHub.

Appendix: source

Thrown at src/Avalonia.Base/Controls/NameScopeExtensions.cs:38

        public static T? Find<T>(this INameScope nameScope, string name)
            where T : class
        {
            _ = nameScope ?? throw new ArgumentNullException(nameof(nameScope));
            _ = name ?? throw new ArgumentNullException(nameof(name));

            var result = nameScope.Find(name);

            if (result == null)
            {
                return null;
            }

            if (result is T typed)
            {
                return typed;
            }

            throw new InvalidOperationException(
                $"Expected control '{name}' to be '{typeof(T)} but it was '{result.GetType()}'.");
        }

        /// <summary>
        /// Finds a named element in an <see cref="INameScope"/>.
        /// </summary>
        /// <typeparam name="T">The element type.</typeparam>
        /// <param name="anchor">The control to take the name scope from.</param>
        /// <param name="name">The name.</param>
        /// <returns>The named element or null if not found.</returns>
        public static T? Find<T>(this ILogical anchor, string name)
            where T : class
        {
            _ = anchor ?? throw new ArgumentNullException(nameof(anchor));
            _ = name ?? throw new ArgumentNullException(nameof(name));
            var styledAnchor = anchor as StyledElement;
            if (styledAnchor == null)
                return null;

View on GitHub (pinned to 11c5427268)