dotnet/aspnetcore · error · InvalidOperationException

The property ' ' on component type ' ' cannot be used with…

Error message

The property '{propertyInfo.Name}' on component type '{targetType.FullName}' cannot be used with 'ParameterAttribute.CaptureUnmatchedValues' because it has the wrong type. The property must be assignable from 'Dictionary<string, object>'.

What it means

ThrowForInvalidCaptureUnmatchedValuesParameterType validates that a property annotated with [Parameter(CaptureUnmatchedValues = true)] is assignable from Dictionary<string, object>. If the property's type is, say, List<string>, object[], or a custom dictionary without the right key/value types, the framework cannot splat arbitrary unmatched attributes into it and throws at cache-construction time.

Solutions

  1. Change the property type to Dictionary<string, object> (the only supported sink type).
  2. If you need typed access, keep the Dictionary<string, object> sink and project its entries into typed fields in OnParametersSet.
  3. Do not use CaptureUnmatchedValues if you cannot accept the required dictionary type.

Example fix

// before
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, string>? Attributes { get; set; } // throws

// after
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? Attributes { get; set; }
Defensive patterns

Strategy: type-guard

Type guard

static bool IsValidCaptureSinkType(PropertyInfo p)
{
    var t = p.PropertyType;
    return typeof(Dictionary<string, object>).IsAssignableFrom(t);
}

var sink = typeof(MyComponent).GetProperties()
    .First(p => p.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true);
Assert.True(IsValidCaptureSinkType(sink));

Prevention

When it happens

Trigger: Declaring [Parameter(CaptureUnmatchedValues = true)] on a property whose type is not assignable from Dictionary<string, object> (e.g., IDictionary<string, string>, Dictionary<string, string>, List<KeyValuePair<string,object>>, object, dynamic). The check happens when the component type's writers cache is built.

Common situations: Wanting strongly-typed attribute bags and using Dictionary<string, string> instead of Dictionary<string, object>. Copying a pattern from a non-Blazor framework that used different collection types. Misunderstanding the required signature of the capture sink.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/b11e2b5f72e939e7. Report an issue: GitHub.

Appendix: source

Thrown at src/Components/Components/src/Reflection/ComponentProperties.cs:270

            if (property.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true)
            {
                propertyNames.Add(property.Name);
            }
        }

        propertyNames.Sort(StringComparer.Ordinal);

        throw new InvalidOperationException(
            $"Multiple properties were found on component type '{targetType.FullName}' with " +
            $"'{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. Only a single property " +
            $"per type can use '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. Properties:" + Environment.NewLine +
            string.Join(Environment.NewLine, propertyNames));
    }

    [DoesNotReturn]
    private static void ThrowForInvalidCaptureUnmatchedValuesParameterType(Type targetType, PropertyInfo propertyInfo)
    {
        throw new InvalidOperationException(
            $"The property '{propertyInfo.Name}' on component type '{targetType.FullName}' cannot be used " +
            $"with '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}' because it has the wrong type. " +
            $"The property must be assignable from 'Dictionary<string, object>'.");
    }

    private sealed class WritersForType
    {
        private const int MaxCachedWriterLookups = 100;
        private readonly Dictionary<string, PropertySetter> _underlyingWriters;
        private readonly ConcurrentDictionary<string, PropertySetter?> _referenceEqualityWritersCache;

        public WritersForType([DynamicallyAccessedMembers(Component)] Type targetType)
        {
            _underlyingWriters = new Dictionary<string, PropertySetter>(StringComparer.OrdinalIgnoreCase);
            _referenceEqualityWritersCache = new ConcurrentDictionary<string, PropertySetter?>(ReferenceEqualityComparer.Instance);

            foreach (var propertyInfo in GetCandidateBindableProperties(targetType))
            {

View on GitHub (pinned to 3600ca084e)