dotnet/aspnetcore · error · InvalidOperationException

A public property ' ' on component type ' ' with a public…

Error message

A public property '{propertyName}' on component type '{type.FullName}' with a public getter wasn't found.

What it means

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.

Solutions

  1. Verify the property name passed to the subscription exactly matches (including casing) a public, gettable property on the component type.
  2. After renaming a component property, update all PersistentValueProviderComponentSubscription registrations referencing it.
  3. Ensure the target property has a public getter.

Example fix

// before
subscription = new PersistentValueProviderComponentSubscription(
    state, logger, serviceProvider, component, propertyName: "UsrName"); // typo

// after
subscription = new PersistentValueProviderComponentSubscription(
    state, logger, serviceProvider, component, propertyName: "UserName");
Defensive patterns

Strategy: validation

Validate before calling

// Verify the property exists and is publicly readable before subscribing.
var prop = component.GetType().GetProperty(propertyName,
    BindingFlags.Public | BindingFlags.Instance);
if (prop?.GetMethod == null || !prop.GetMethod.IsPublic)
{
    throw new ArgumentException($"No public getter '{propertyName}' on {component.GetType().FullName}");
}
var subscription = new PersistentValueProviderComponentSubscription(state, logger, services, component, propertyName);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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

Appendix: source

Thrown at src/Components/Components/src/PersistentState/PersistentValueProviderComponentSubscription.cs:234

        var serializerType = typeof(PersistentComponentStateSerializer<>).MakeGenericType(type);
        var serializer = serviceProvider.GetService(serializerType);

        // The generic class now inherits from the internal interface, so we can cast directly
        return serializer as IPersistentComponentStateSerializer;
    }

    [UnconditionalSuppressMessage(
    "Trimming",
    "IL2077:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The source field does not have matching annotations.",
    Justification = "Properties of rendered components are preserved through other means and won't get trimmed.")]

    private static PropertyGetter PropertyGetterFactory((Type type, string propertyName) key)
    {
        var (type, propertyName) = key;
        var propertyInfo = GetPropertyInfo(type, propertyName);
        if (propertyInfo == null || propertyInfo.GetMethod == null || !propertyInfo.GetMethod.IsPublic)
        {
            throw new InvalidOperationException(
                $"A public property '{propertyName}' on component type '{type.FullName}' with a public getter wasn't found.");
        }

        return new PropertyGetter(type, propertyInfo);

        static PropertyInfo? GetPropertyInfo([DynamicallyAccessedMembers(LinkerFlags.Component)] Type type, string propertyName)
            => type.GetProperty(propertyName);
    }

    private static partial class Log
    {
        [LoggerMessage(1, LogLevel.Debug, "Persisting value for storage key '{StorageKey}' of type '{PropertyType}' from component '{ComponentType}' for property '{PropertyName}'", EventName = "PersistingValueToState")]
        public static partial void PersistingValueToState(ILogger logger, string storageKey, string propertyType, string componentType, string propertyName);

        [LoggerMessage(2, LogLevel.Debug, "Skipped persisting null value for storage key '{StorageKey}' of type '{PropertyType}' from component '{ComponentType}' for property '{PropertyName}'", EventName = "SkippedPersistingNullValue")]
        public static partial void SkippedPersistingNullValue(ILogger logger, string storageKey, string propertyType, string componentType, string propertyName);

        [LoggerMessage(3, LogLevel.Debug, "Restoring value for storage key '{StorageKey}' of type '{PropertyType}' for property '{PropertyName}'", EventName = "RestoringValueFromState")]

View on GitHub (pinned to 3600ca084e)