dotnet/aspnetcore · error · InvalidOperationException

Cannot update existing state: previous state has not been cl

Error message

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

What it means

Thrown by PersistentComponentState.UpdateExistingState when _existingState is null (state was never initialized via InitializeExistingState) OR when _existingState.Count > 0 (previously restored entries were never consumed via TryTake). UpdateExistingState is called from RestoreStateAsync only when the context is RestoreContext.ValueUpdate, i.e. during an in-place value update rather than the initial restore.

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 294cab2f9b)

Solutions

  1. Ensure the initial RestoreStateAsync(store) (default RestoreContext.InitialValue) runs exactly once before any RestoreContext.ValueUpdate restore.
  2. Make sure every persisted key is consumed (TryTake*) on each cycle so _existingState is empty before the next update.
  3. If some keys must survive across updates, restructure the store to return a fresh dictionary for value updates instead of relying on leftover entries.
  4. Audit restoring callbacks to confirm they all call TryTakeFromJson/TryTakeBytes for their keys.

Example fix

// before: restoring callback registered but never consumes its key
state.RegisterOnRestoring(() => { /* reads _local, never calls TryTake */ });

// after: consume the key on every restore
state.RegisterOnRestoring(() =>
{
    if (state.TryTakeFromJson<Cart>("cart", out var restored))
        _local = restored;
});
Defensive patterns

Strategy: validation

Validate before calling

// In a custom store/host: ensure the initial restore ran and prior state was consumed
// before issuing a ValueUpdate restore.
bool CanValueUpdate(ComponentStatePersistenceManager m)
    => m.State.CurrentContext != RestoreContext.InitialValue; // simplistic gate

// Stronger: ensure your restoring callbacks always consume their keys so existingState empties.

Try / catch

try
{
    await manager.RestoreStateAsync(store, RestoreContext.ValueUpdate);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Cannot update existing state"))
{
    // state never initialized or not fully consumed - reinitialize instead
    await manager.RestoreStateAsync(store); // fresh initial restore
}

Prevention

When it happens

Trigger: Calling RestoreStateAsync(store, RestoreContext.ValueUpdate) before the initial RestoreStateAsync(store) has run; or after a restore where some persisted keys were never consumed by TryTakeFromJson/TryTakeBytes, leaving _existingState non-empty when the next ValueUpdate arrives.

Common situations: A custom IPersistentComponentStateStore or host that drives multiple restore cycles (e.g. streaming updates) but skips the initial restore; a persistent service/component that registers a restoring callback but does not consume its key on update; partial consumption of state across value-update cycles.

Related errors


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