dotnet/aspnetcore · error · InvalidOperationException
The type ' ' declares a property matching the name ' ' that…
Error message
The type '{targetType.FullName}' declares a property matching the name '{propertyName}' that is not public. Persistent service properties must be public. What it means
PersistentServicesRegistry.PropertiesAccessor scans a persistent service type for properties annotated with [PersistentState]. For each such property it requires a public setter so that restoration can write the deserialized value back. If propertyInfo.SetMethod is null (no setter) or not public (private/protected/internal setter), restoration cannot inject the value and the registry throws during service registration.
Solutions
- Make the setter public: change 'private set' to 'public set' on the [PersistentState] property.
- For init-only patterns, use 'public init' so the property is publicly settable during restoration.
- If the property must stay encapsulated, introduce a separate public property backed by the persistent state, or apply [PersistentState] to a public property that delegates to the encapsulated field.
Example fix
// before
[PersistentState] public int Count { get; private set; }
// after
[PersistentState] public int Count { get; set; } Defensive patterns
Strategy: type-guard
Type guard
static bool HasPublicSetter(PropertyInfo p)
=> p.SetMethod != null && p.SetMethod.IsPublic;
// In a unit test for persistent service types:
foreach (var p in typeof(MyService).GetProperties())
{
if (p.GetCustomAttribute<PersistentStateAttribute>() != null)
Assert.True(HasPublicSetter(p));
} Prevention
- When applying [PersistentState], make both getter and setter public.
- Add a unit test asserting all [PersistentState] properties have public accessors.
- Avoid private setters on persistent service state; validate in OnInitialized instead.
When it happens
Trigger: Declaring a property with [PersistentState] that has a private or protected setter, an init-only setter that is non-public, or no setter at all (read-only/auto-get-only). Registering such a type via PersistentServicesRegistry causes the PropertiesAccessor constructor to throw on first restoration.
Common situations: Encapsulating persistent service state with a private setter to enforce invariants (common DDD pattern) but still wanting it restored. Using { get; init; } without making the init accessor public. Migrating a service to persistent-state attributes and forgetting to expose the setter.
Related errors
- A public property ' ' on component type ' ' with a public…
- Cannot update existing state: previous state has not been…
- Multiple properties were found on component type
- Object of type ' ' does not have a property matching the…
- Object of type ' ' has a property matching the name ' '…
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/e811946a5f8b191f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs:191
foreach (var attribute in propertyInfo.GetCustomAttributes())
{
if (attribute is PersistentStateAttribute persistentStateAttribute)
{
parameterAttribute = persistentStateAttribute;
break;
}
}
if (parameterAttribute == null)
{
continue;
}
var propertyName = propertyInfo.Name;
var key = ComputeKey(keyType, propertyName);
keys.Add(new(key, propertyInfo.PropertyType));
if (propertyInfo.SetMethod == null || !propertyInfo.SetMethod.IsPublic)
{
throw new InvalidOperationException(
$"The type '{targetType.FullName}' declares a property matching the name '{propertyName}' that is not public. Persistent service properties must be public.");
}
if (propertyInfo.GetMethod == null || !propertyInfo.GetMethod.IsPublic)
{
throw new InvalidOperationException(
$"The type '{targetType.FullName}' declares a property matching the name '{propertyName}' that is not public. Persistent service properties must be public.");
}
var restoreOptions = new RestoreOptions
{
RestoreBehavior = parameterAttribute.RestoreBehavior,
AllowUpdates = parameterAttribute.AllowUpdates,
};
var propertySetter = new PropertySetter(targetType, propertyInfo);
var propertyGetter = new PropertyGetter(targetType, propertyInfo);
View on GitHub (pinned to 3600ca084e)