{"record":{"id":"03f4bf0ebac1d056","repo":"dotnet/aspnetcore","slug":"a-public-property-propertyname-on-component-ty","errorCode":null,"errorMessage":"A public property '{propertyName}' on component type '{type.FullName}' with a public getter wasn't found.","messagePattern":"A public property '(.+?)' on component type '(.+?)' with a public getter wasn't found\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs","lineNumber":234,"sourceCode":"        var serializerType = typeof(PersistentComponentStateSerializer<>).MakeGenericType(type);\n        var serializer = serviceProvider.GetService(serializerType);\n\n        // The generic class now inherits from the internal interface, so we can cast directly\n        return serializer as IPersistentComponentStateSerializer;\n    }\n\n    [UnconditionalSuppressMessage(\n    \"Trimming\",\n    \"IL2077:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The source field does not have matching annotations.\",\n    Justification = \"Properties of rendered components are preserved through other means and won't get trimmed.\")]\n\n    private static PropertyGetter PropertyGetterFactory((Type type, string propertyName) key)\n    {\n        var (type, propertyName) = key;\n        var propertyInfo = GetPropertyInfo(type, propertyName);\n        if (propertyInfo == null || propertyInfo.GetMethod == null || !propertyInfo.GetMethod.IsPublic)\n        {\n            throw new InvalidOperationException(\n                $\"A public property '{propertyName}' on component type '{type.FullName}' with a public getter wasn't found.\");\n        }\n\n        return new PropertyGetter(type, propertyInfo);\n\n        static PropertyInfo? GetPropertyInfo([DynamicallyAccessedMembers(LinkerFlags.Component)] Type type, string propertyName)\n            => type.GetProperty(propertyName);\n    }\n\n    private static partial class Log\n    {\n        [LoggerMessage(1, LogLevel.Debug, \"Persisting value for storage key '{StorageKey}' of type '{PropertyType}' from component '{ComponentType}' for property '{PropertyName}'\", EventName = \"PersistingValueToState\")]\n        public static partial void PersistingValueToState(ILogger logger, string storageKey, string propertyType, string componentType, string propertyName);\n\n        [LoggerMessage(2, LogLevel.Debug, \"Skipped persisting null value for storage key '{StorageKey}' of type '{PropertyType}' from component '{ComponentType}' for property '{PropertyName}'\", EventName = \"SkippedPersistingNullValue\")]\n        public static partial void SkippedPersistingNullValue(ILogger logger, string storageKey, string propertyType, string componentType, string propertyName);\n\n        [LoggerMessage(3, LogLevel.Debug, \"Restoring value for storage key '{StorageKey}' of type '{PropertyType}' for property '{PropertyName}'\", EventName = \"RestoringValueFromState\")]","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs#L216-L252","documentation":"Thrown by PersistentValueProviderComponentSubscription.PropertyGetterFactory when resolving the getter for a [PersistentState] cascading parameter on a component and the property info is null or its GetMethod is null/non-public. The subscription needs to read the current property value to persist it across render cycles.","triggerScenarios":"A component declares a [PersistentState] cascading parameter whose getter is non-public, or the property cannot be resolved by name on the runtime component type.","commonSituations":"A [PersistentState] cascading parameter declared with a non-public getter; a property renamed while the persisted attribute still references the old name; a custom component subclass that hides the getter.","solutions":["Ensure the [PersistentState] cascading parameter has a public getter.","Verify the property name on the component matches what the attribute/parameter info expects.","Avoid hiding the getter in derived component types.","Audit component [PersistentState] parameters for public getters."],"exampleFix":"// before\n[CascadingParameter] [PersistentState]\npublic User CurrentUser { get; private set; } // non-public getter on a value-provider param\n\n// after\n[CascadingParameter] [PersistentState]\npublic User CurrentUser { get; set; }","handlingStrategy":"validation","validationCode":"// Audit component types that use [PersistentState] cascading parameters for a public getter.\nstatic void ValidateComponentPersistentStateParams(params Type[] componentTypes)\n{\n    foreach (var t in componentTypes)\n    foreach (var p in t.GetProperties(\n        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))\n    {\n        if (p.GetCustomAttribute<PersistentStateAttribute>() is null) continue;\n        if (p.GetGetMethod(nonPublic: false) is null)\n            throw new InvalidOperationException(\n                $\"Component {t.FullName}.{p.Name} [PersistentState] needs a public getter.\");\n    }\n}","typeGuard":"static bool ComponentHasPublicGetter(Type componentType, string propertyName)\n    => componentType.GetProperty(propertyName)?.GetGetMethod(nonPublic: false) is not null;","tryCatchPattern":null,"preventionTips":["Give every [PersistentState] cascading parameter a public getter.","Keep property names stable; avoid renaming persisted parameters without updating attributes.","Do not hide the getter in derived component types.","Audit component [PersistentState] parameters during build/startup."],"tags":["blazor","component-state","persistence","cascading-parameter","reflection","visibility"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}