{"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/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs#L216-L252","documentation":"PersistentValueProviderComponentSubscription.PropertyGetterFactory resolves a PropertyGetter for a (type, propertyName) pair during value-provider persistence. If the property does not exist on the component type, has no getter, or its getter is not public, it cannot be read for serialization and the subscription throws. The factory is called when a [PersistentState] value-provider subscription attempts to persist the current value of the bound component property.","triggerScenarios":"Configuring a PersistentValueProviderComponentSubscription (via the value-provider extensibility API) with a property name that does not match any public readable property on the target component type. Renaming a component property without updating the subscription's PropertyName. Binding a subscription to a write-only or non-public property.","commonSituations":"Typo or casing mismatch in the property name supplied to the value-provider subscription. Refactoring a component and renaming the bound property while the subscription still references the old name. Applying a value provider to a property that was made non-public during a redesign.","solutions":["Verify the property name passed to the subscription exactly matches (including casing) a public, gettable property on the component type.","After renaming a component property, update all PersistentValueProviderComponentSubscription registrations referencing it.","Ensure the target property has a public getter."],"exampleFix":"// before\nsubscription = new PersistentValueProviderComponentSubscription(\n    state, logger, serviceProvider, component, propertyName: \"UsrName\"); // typo\n\n// after\nsubscription = new PersistentValueProviderComponentSubscription(\n    state, logger, serviceProvider, component, propertyName: \"UserName\");","handlingStrategy":"validation","validationCode":"// Verify the property exists and is publicly readable before subscribing.\nvar prop = component.GetType().GetProperty(propertyName,\n    BindingFlags.Public | BindingFlags.Instance);\nif (prop?.GetMethod == null || !prop.GetMethod.IsPublic)\n{\n    throw new ArgumentException($\"No public getter '{propertyName}' on {component.GetType().FullName}\");\n}\nvar subscription = new PersistentValueProviderComponentSubscription(state, logger, services, component, propertyName);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep subscription property names in sync with component property names.","Add a compile-time or test-time check mapping subscriptions to properties.","After renaming a component property, grep for subscription registrations."],"tags":["blazor","persistent-state","reflection","value-provider"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}