dotnet/aspnetcore · error · InvalidOperationException

State already persisted.

Error message

State already persisted.

What it means

ComponentStatePersistenceManager.PersistStateAsync enforces a single-persistence contract via the _stateIsPersisted flag. Once PersistStateAsync has completed (the flag is set true at the end of PauseAndPersistState), a second call throws. Persistence is a destructive, terminal operation: it pauses state, drains registered callbacks into _currentState, writes to the store, and clears working buffers, so repeating it would produce partial or empty output.

Solutions

  1. Ensure PersistStateAsync is invoked exactly once per manager lifetime; track persistence state in your host code if multiple paths could reach it.
  2. For repeated persistence (e.g., tests), create a new ComponentStatePersistenceManager for each cycle.
  3. Audit middleware/endpoint order so only the framework-owned persistence hook calls PersistStateAsync.

Example fix

// before: persisted twice
await manager.PersistStateAsync(store, renderer);
await manager.PersistStateAsync(store, renderer); // throws

// after: single persistence, or new manager per cycle
await manager.PersistStateAsync(store, renderer);
// for a new cycle, construct a fresh manager
Defensive patterns

Strategy: validation

Validate before calling

if (!_persisted)
{
    await manager.PersistStateAsync(store, renderer);
    _persisted = true;
}

Prevention

When it happens

Trigger: Calling PersistStateAsync(store, renderer) twice on the same manager instance. A host/endpoint pipeline that runs the persist step more than once for a single render (e.g., both a response-writing middleware and a developer's manual call). Persisting after a composite-store persistence loop already completed.

Common situations: Adding custom middleware that also persists component state alongside the framework's built-in persistence. Endpoints that render and persist, then a fallback path re-invokes persistence on exception handling. Integration tests that reuse a manager across multiple render-then-persist cycles.

Related errors


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

Appendix: 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 3600ca084e)