{"record":{"id":"b11e2b5f72e939e7","repo":"dotnet/aspnetcore","slug":"the-property-propertyinfo-name-on-component-ty","errorCode":null,"errorMessage":"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>'.","messagePattern":"The property '(.+?)' on component type '(.+?)' cannot be used with 'ParameterAttribute\\.CaptureUnmatchedValues' because it has the wrong type\\. The property must be assignable from 'Dictionary<string, object>'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/Reflection/ComponentProperties.cs","lineNumber":270,"sourceCode":"            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;\n        private readonly Dictionary<string, PropertySetter> _underlyingWriters;\n        private readonly ConcurrentDictionary<string, PropertySetter?> _referenceEqualityWritersCache;\n\n        public WritersForType([DynamicallyAccessedMembers(Component)] Type targetType)\n        {\n            _underlyingWriters = new Dictionary<string, PropertySetter>(StringComparer.OrdinalIgnoreCase);\n            _referenceEqualityWritersCache = new ConcurrentDictionary<string, PropertySetter?>(ReferenceEqualityComparer.Instance);\n\n            foreach (var propertyInfo in GetCandidateBindableProperties(targetType))\n            {","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/Reflection/ComponentProperties.cs#L252-L288","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change the property type to Dictionary<string, object> (the only supported sink type).","If you need typed access, keep the Dictionary<string, object> sink and project its entries into typed fields in OnParametersSet.","Do not use CaptureUnmatchedValues if you cannot accept the required dictionary type."],"exampleFix":"// before\n[Parameter(CaptureUnmatchedValues = true)]\npublic Dictionary<string, string>? Attributes { 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 IsValidCaptureSinkType(PropertyInfo p)\n{\n    var t = p.PropertyType;\n    return typeof(Dictionary<string, object>).IsAssignableFrom(t);\n}\n\nvar sink = typeof(MyComponent).GetProperties()\n    .First(p => p.GetCustomAttribute<ParameterAttribute>()?.CaptureUnmatchedValues == true);\nAssert.True(IsValidCaptureSinkType(sink));","tryCatchPattern":null,"preventionTips":["Use Dictionary<string, object> as the capture-sink type.","Add a unit test verifying the sink type is assignable from Dictionary<string, object>.","Project typed values from the dictionary in OnParametersSet rather than changing the sink type."],"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"}