dotnet/aspnetcore · error · InvalidOperationException

Multiple properties were found on component type '{targetTyp

Error message

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:

What it means

Thrown by ComponentProperties.ThrowForMultipleCaptureUnmatchedValuesParameters when more than one property on a component is decorated with [Parameter(CaptureUnmatchedValues = true)]. Only a single catch-all parameter is permitted per type, including across the inheritance chain.

Source

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

            $"when also used to capture unmatched values. Unmatched values:" + Environment.NewLine +
            string.Join(Environment.NewLine, unmatched.Keys));
    }

    [DoesNotReturn]
    private static void ThrowForMultipleCaptureUnmatchedValuesParameters([DynamicallyAccessedMembers(Component)] Type targetType)
    {
        var propertyNames = new List<string>();
        foreach (var property in targetType.GetProperties(BindablePropertyFlags))
        {
            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;

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Keep exactly one CaptureUnmatchedValues property per component, including inherited ones.
  2. If a base type already declares it, do not redeclare it in the derived type.
  3. Consolidate the two catch-all properties into a single Dictionary<string, object> parameter.

Example fix

// before
[Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object> A { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object> B { get; set; }

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

Strategy: validation

Validate before calling

// Ensure at most one CaptureUnmatchedValues parameter per type (including base types).
static int CountCaptureUnmatchedValues(Type t)
{
    int n = 0;
    for (var type = t; type is not null; type = type.BaseType)
    foreach (var p in type.GetProperties(
        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
    {
        if (p.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true) n++;
    }
    return n;
}

Type guard

static bool HasSingleCaptureUnmatchedValues(Type t) => CountCaptureUnmatchedValues(t) <= 1;

Prevention

When it happens

Trigger: Two properties on the same component both declared [Parameter(CaptureUnmatchedValues = true)]; a base component declares one and a derived component declares another.

Common situations: Copy-paste of a catch-all parameter; inheriting a base with a catch-all and adding another in the derived type; merging two components that each had their own catch-all.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/07685eb07761672d. Report an issue: GitHub.