dotnet/aspnetcore · error · InvalidOperationException

State already initialized.

Error message

State already initialized.

What it means

Thrown by ComponentStatePersistenceManager.RestoreStateAsync when _stateIsInitialized is already true and the supplied RestoreContext is not ValueUpdate. The manager permits re-entry only for RestoreContext.ValueUpdate (which routes to State.UpdateExistingState); any other context on an already-initialized manager is treated as a misuse.

Source

Thrown at src/Components/Components/src/PersistentState/ComponentStatePersistenceManager.cs:78

    {
        await RestoreStateAsync(store, RestoreContext.InitialValue);
    }

    /// <summary>
    /// Restores the application state.
    /// </summary>
    /// <param name="store"> The <see cref="IPersistentComponentStateStore"/> to restore the application state from.</param>
    /// <param name="context">The <see cref="RestoreContext"/> that provides additional context for the restoration.</param>
    /// <returns>A <see cref="Task"/> that will complete when the state has been restored.</returns>
    public async Task RestoreStateAsync(IPersistentComponentStateStore store, RestoreContext context)
    {
        var data = await store.GetPersistedStateAsync();

        if (_stateIsInitialized)
        {
            if (context != RestoreContext.ValueUpdate)
            {
                throw new InvalidOperationException("State already initialized.");
            }
            State.UpdateExistingState(data, context);
            foreach (var registration in _registeredRestoringCallbacks)
            {
                registration.Callback();
            }
        }
        else
        {
            State.InitializeExistingState(data, context);
            _servicesRegistry?.RegisterForPersistence(State);
            _stateIsInitialized = true;
        }
    }

    /// <summary>
    /// Persists the component application state into the given <see cref="IPersistentComponentStateStore"/>.
    /// </summary>

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Call the initial RestoreStateAsync exactly once per manager lifetime; gate with your own flag or rely on DI scoped/singleton semantics.
  2. For subsequent in-place updates pass RestoreContext.ValueUpdate explicitly.
  3. If a fresh restore is genuinely needed, use a new ComponentStatePersistenceManager instance (new DI scope / new circuit).
  4. Confirm framework integration isn't already restoring before your code does.

Example fix

// before
await manager.RestoreStateAsync(store); // called on every request -> throws 2nd time

// after
if (!_restored)
{
    await manager.RestoreStateAsync(store);
    _restored = true;
}
else
{
    await manager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
}
Defensive patterns

Strategy: validation

Validate before calling

private bool _restored;

async Task RestoreOnceAsync(ComponentStatePersistenceManager manager, IPersistentComponentStateStore store)
{
    if (_restored)
    {
        // subsequent updates must use ValueUpdate
        await manager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
        return;
    }
    await manager.RestoreStateAsync(store);
    _restored = true;
}

Try / catch

try
{
    await manager.RestoreStateAsync(store);
}
catch (InvalidOperationException ex) when (ex.Message == "State already initialized.")
{
    // already restored - route through ValueUpdate instead
    await manager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
}

Prevention

When it happens

Trigger: Calling RestoreStateAsync(store) (default context) a second time on the same ComponentStatePersistenceManager instance; middleware that restores on every request without gating on initialization.

Common situations: Custom host/middleware that wires restore per-request without a once-per-lifetime guard; a test that reuses one manager across requests; double-registration of restore middleware in the pipeline.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/ca6ea4a5200f0dfa. Report an issue: GitHub.