dotnet/aspnetcore · error · InvalidOperationException

Cannot update existing state: previous state has not been…

Error message

Cannot update existing state: previous state has not been cleared or state is not initialized.

What it means

PersistentComponentState.UpdateExistingState is an internal method invoked during state restoration with RestoreContext.ValueUpdate. It refuses to install new state when the existing state dictionary is null (never initialized via InitializeExistingState) or still contains unconsumed entries (Count > 0). The guard prevents silently discarding or merging leftover persisted entries, which would corrupt the restore contract that expects each key to be consumed exactly once via TryTake.

Solutions

  1. Ensure RestoreStateAsync(store, RestoreContext.InitialValue) runs to completion exactly once before any RestoreContext.ValueUpdate restore so _existingState becomes non-null.
  2. Consume or clear all entries from the current state (every registered restoring callback must call TryTake for its keys) before invoking a ValueUpdate restore.
  3. If you hold a reference to PersistentComponentState, verify State exists and drain it before the update; do not call the internal UpdateExistingState directly.

Example fix

// before: second restore with ValueUpdate before initial restore finished
await manager.RestoreStateAsync(store, RestoreContext.ValueUpdate);

// after: initialize first, then update only when prior state is drained
await manager.RestoreStateAsync(store, RestoreContext.InitialValue);
// ... components consume their keys via TryTake ...
await manager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
Defensive patterns

Strategy: validation

Validate before calling

// Validate before triggering a ValueUpdate restore.
// Ensure initial restore ran and existing state is empty.
if (!stateInitialized)
{
    await manager.RestoreStateAsync(store, RestoreContext.InitialValue);
}
// Drain prior keys before the update pass
foreach (var key in requiredKeys)
{
    state.TryTake(key, out _);
}

Prevention

When it happens

Trigger: RestoreStateAsync(store, RestoreContext.ValueUpdate) is called before any prior RestoreStateAsync(store, RestoreContext.InitialValue) ran (so _existingState is still null), or it is called while previously-restored entries have not yet been consumed via PersistentComponentState.TryTake/TryTakeBytes. Calling UpdateExistingState directly with a non-empty current state also triggers it.

Common situations: A custom IPersistentComponentStateStore or host pipeline that drives a second restore pass for value updates without first performing the initial restore. Persisting a value provider subscription whose restoring callback re-enters RestoreStateAsync while prior keys remain unconsumed. Misordered startup hooks in a Blazor Web App that interleave prerender restore and interactive restore incorrectly.

Related errors


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

Appendix: source

Thrown at src/Components/Components/src/PersistentComponentState.cs:249

        if (_existingState.TryGetValue(key, out value))
        {
            _existingState.Remove(key);
            return true;
        }
        else
        {
            return false;
        }
    }

    internal void UpdateExistingState(IDictionary<string, byte[]> state, RestoreContext context)
    {
        ArgumentNullException.ThrowIfNull(state);

        if (_existingState == null || _existingState.Count > 0)
        {
            throw new InvalidOperationException("Cannot update existing state: previous state has not been cleared or state is not initialized.");
        }

        _existingState = state;
        CurrentContext = context;
    }
}

View on GitHub (pinned to 3600ca084e)