{"record":{"id":"097fdc0bb4ee72da","repo":"dotnet/aspnetcore","slug":"multiple-properties-were-found-on-component-type","errorCode":null,"errorMessage":"Multiple properties were found on component type '{targetType.FullName}' with 'ParameterAttribute.CaptureUnmatchedValues'. Only a single property per type can use 'ParameterAttribute.CaptureUnmatchedValues'. Properties:","messagePattern":"Multiple properties were found on component type '(.+?)' with 'ParameterAttribute\\.CaptureUnmatchedValues'\\. Only a single property per type can use 'ParameterAttribute\\.CaptureUnmatchedValues'\\. Properties:","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/Reflection/ComponentProperties.cs","lineNumber":260,"sourceCode":"            $\"when also used to capture unmatched values. Unmatched values:\" + Environment.NewLine +\n            string.Join(Environment.NewLine, unmatched.Keys));\n    }\n\n    [DoesNotReturn]\n    private static void ThrowForMultipleCaptureUnmatchedValuesParameters([DynamicallyAccessedMembers(Component)] Type targetType)\n    {\n        var propertyNames = new List<string>();\n        foreach (var property in targetType.GetProperties(BindablePropertyFlags))\n        {\n            if (property.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true)\n            {\n                propertyNames.Add(property.Name);\n            }\n        }\n\n        propertyNames.Sort(StringComparer.Ordinal);\n\n        throw new InvalidOperationException(\n            $\"Multiple properties were found on component type '{targetType.FullName}' with \" +\n            $\"'{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. Only a single property \" +\n            $\"per type can use '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. Properties:\" + Environment.NewLine +\n            string.Join(Environment.NewLine, propertyNames));\n    }\n\n    [DoesNotReturn]\n    private static void ThrowForInvalidCaptureUnmatchedValuesParameterType(Type targetType, PropertyInfo propertyInfo)\n    {\n        throw new InvalidOperationException(\n            $\"The property '{propertyInfo.Name}' on component type '{targetType.FullName}' cannot be used \" +\n            $\"with '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}' because it has the wrong type. \" +\n            $\"The property must be assignable from 'Dictionary<string, object>'.\");\n    }\n\n    private sealed class WritersForType\n    {\n        private const int MaxCachedWriterLookups = 100;","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/Reflection/ComponentProperties.cs#L242-L278","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Keep only one property with [Parameter(CaptureUnmatchedValues = true)] per component type; remove the attribute from the others.","If inheritance introduced a second sink, remove it from the derived type or break the inheritance.","Merge unmatched attributes into a single Dictionary<string, object> property."],"exampleFix":"// before\n[Parameter(CaptureUnmatchedValues = true)]\npublic Dictionary<string, object>? Attributes { get; set; }\n[Parameter(CaptureUnmatchedValues = true)]\npublic Dictionary<string, object>? Extra { get; set; } // throws\n\n// after\n[Parameter(CaptureUnmatchedValues = true)]\npublic Dictionary<string, object>? Attributes { get; set; }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static bool HasSingleCaptureSink(Type componentType)\n{\n    int count = componentType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)\n        .Count(p => p.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true);\n    return count <= 1;\n}\n\nAssert.True(HasSingleCaptureSink(typeof(MyComponent)));","tryCatchPattern":null,"preventionTips":["Keep exactly one CaptureUnmatchedValues property per component (including inherited ones).","Add a unit test asserting the single-sink invariant per component type.","When inheriting, verify the base type does not already declare a sink."],"tags":["blazor","parameters","capture-unmatched","reflection"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}