dotnet/aspnetcore · error · InvalidOperationException
The type '{targetType.FullName}' declares a property matchin
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
Thrown by PersistentServicesRegistry.PropertiesAccessor when a property decorated with [PersistentStateAttribute] has a setter that is null or non-public. Persistent service properties must expose a public setter so the framework can write restored values back via PropertySetter during restore.
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 294cab2f9b)
Solutions
- Make the setter public: { get; set; }.
- If the property must not be publicly settable, remove [PersistentState] since the framework cannot restore into it.
- For records, declare the property with a public set accessor.
- Run a quick reflection audit over registered service types to catch non-public setters before runtime.
Example fix
// before
[PersistentState]
public Cart Cart { get; private set; }
// after
[PersistentState]
public Cart Cart { get; set; } Defensive patterns
Strategy: validation
Validate before calling
// Reflection audit: ensure every [PersistentState] property on a persistent service
// has a public setter before the app starts.
static void ValidatePersistentServices(params Type[] types)
{
foreach (var t in types)
foreach (var p in t.GetProperties())
{
if (p.GetCustomAttribute<PersistentStateAttribute>() is null) continue;
if (p.GetSetMethod(nonPublic: false) is null)
throw new InvalidOperationException(
$"{t.FullName}.{p.Name} has [PersistentState] but no public setter.");
}
} Type guard
static bool HasPublicSetter(PropertyInfo p) => p.GetSetMethod(nonPublic: false) is not null;
Prevention
- Use { get; set; } for every [PersistentState] property on a persistent service.
- Avoid { get; private set; }, { get; init; }, and get-only properties for persisted service state.
- Run a startup reflection audit over registered persistent service types.
- For records, declare persisted properties with explicit public setters.
When it happens
Trigger: Declaring [PersistentState] on a read-only property (no setter) or a property with a private/protected/internal setter on a service registered via AddPersistentService<T>.
Common situations: Using init-only or get-only properties ({ get; init; } / { get; }); { get; private set; } for encapsulation; record primary constructors producing non-public setters.
Related errors
- A public property '{propertyName}' on component type '{type.
- Cannot update existing state: previous state has not been cl
- State already initialized.
- State already persisted.
- The registered callback {registration.Callback.Method.Name}
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/e811946a5f8b191f.
Report an issue: GitHub.