dotnet/aspnetcore · error · InvalidOperationException
State already persisted.
Error message
State already persisted.
What it means
Thrown by ComponentStatePersistenceManager.PersistStateAsync when _stateIsPersisted is true. The flag is set at the end of PauseAndPersistState and never reset, so the manager is single-use for persistence within its lifetime.
Source
Thrown at src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs:104
else
{
State.InitializeExistingState(data, context);
_servicesRegistry?.RegisterForPersistence(State);
_stateIsInitialized = true;
}
}
/// <summary>
/// Persists the component application state into the given <see cref="IPersistentComponentStateStore"/>.
/// </summary>
/// <param name="store">The <see cref="IPersistentComponentStateStore"/> to restore the application state from.</param>
/// <param name="renderer">The <see cref="Renderer"/> that components are being rendered.</param>
/// <returns>A <see cref="Task"/> that will complete when the state has been restored.</returns>
public Task PersistStateAsync(IPersistentComponentStateStore store, Renderer renderer)
{
if (_stateIsPersisted)
{
throw new InvalidOperationException("State already persisted.");
}
return renderer.Dispatcher.InvokeAsync(PauseAndPersistState);
async Task PauseAndPersistState()
{
State.PersistingState = true;
if (store is IEnumerable<IPersistentComponentStateStore> compositeStore)
{
// We only need to do inference when there is more than one store. This is determined by
// the set of rendered components.
InferRenderModes(renderer);
// Iterate over each store and give it a chance to run against the existing declared
// render modes. After we've run through a store, we clear the current state so that
// the next store can start with a clean slate.
foreach (var store in compositeStore)View on GitHub (pinned to 294cab2f9b)
Solutions
- Persist once per manager/circuit lifetime; guard with your own flag.
- Use a new ComponentStatePersistenceManager (new DI scope) for each persistence cycle if you need to persist again.
- In hosted scenarios rely on the framework's single PersistStateAsync call during prerender rather than calling it yourself again.
- Audit the request pipeline for duplicate persist invocations.
Example fix
// before
await manager.PersistStateAsync(store, renderer); // called twice -> throws
// after
if (!_persisted)
{
await manager.PersistStateAsync(store, renderer);
_persisted = true;
} Defensive patterns
Strategy: validation
Validate before calling
private bool _persisted;
async Task PersistOnceAsync(ComponentStatePersistenceManager manager,
IPersistentComponentStateStore store, Renderer renderer)
{
if (_persisted) return;
await manager.PersistStateAsync(store, renderer);
_persisted = true;
} Try / catch
try
{
await manager.PersistStateAsync(store, renderer);
}
catch (InvalidOperationException ex) when (ex.Message == "State already persisted.")
{
// already persisted this cycle - nothing to do
} Prevention
- Persist exactly once per manager/circuit lifetime; guard with a flag.
- Rely on the framework's prerender persist call rather than calling PersistStateAsync yourself again.
- Use a new manager/scope if a second persistence cycle is genuinely needed.
- Audit the request pipeline for duplicate persist calls.
When it happens
Trigger: Calling PersistStateAsync(store, renderer) twice on the same manager instance.
Common situations: Middleware/endpoints invoking persist on each render without a fresh manager; tests reusing the manager; an integration that triggers persist both for prerender and for the final response.
Related errors
- Cannot update existing state: previous state has not been cl
- State already initialized.
- The registered callback {registration.Callback.Method.Name}
- The type '{targetType.FullName}' declares a property matchin
- A public property '{propertyName}' on component type '{type.
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/bf4dba6a50bf5325.
Report an issue: GitHub.