dotnet/aspnetcore · error · InvalidOperationException

Multiple properties were found on component type

Error message

Multiple properties were found on component type '{targetType.FullName}' with 'ParameterAttribute.CaptureUnmatchedValues'. Only a single property per type can use 'ParameterAttribute.CaptureUnmatchedValues'. Properties:

What it means

ThrowForMultipleCaptureUnmatchedValuesParameters scans a component type during cache construction and throws if more than one property is annotated with [Parameter(CaptureUnmatchedValues = true)]. Blazor allows exactly one unmatched-attributes sink per component; ambiguity about which property receives splatted attributes is a design error. The message lists the offending property names sorted ordinally.

Solutions

  1. Keep only one property with [Parameter(CaptureUnmatchedValues = true)] per component type; remove the attribute from the others.
  2. If inheritance introduced a second sink, remove it from the derived type or break the inheritance.
  3. Merge unmatched attributes into a single Dictionary<string, object> property.

Example fix

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

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

Strategy: type-guard

Type guard

static bool HasSingleCaptureSink(Type componentType)
{
    int count = componentType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
        .Count(p => p.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true);
    return count <= 1;
}

Assert.True(HasSingleCaptureSink(typeof(MyComponent)));

Prevention

When it happens

Trigger: Declaring two (or more) properties each with [Parameter(CaptureUnmatchedValues = true)] on the same component type. Copy-pasting a capture-sink property and forgetting to remove the original. Inheritance where both base and derived declare a capture sink.

Common situations: Component library authors adding a second splat sink by mistake. Refactoring that duplicates the Attributes property. Inherited components where the derived type adds its own sink alongside an inherited one.

Related errors


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

Appendix: 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 3600ca084e)