dotnet/aspnetcore · error · InvalidOperationException

Registering a callback while persisting state is not allowed

Error message

Registering a callback while persisting state is not allowed.

What it means

RegisterOnPersisting throws InvalidOperationException if called while PersistingState is true. Persisting callbacks can only be registered before the persistence phase begins; registering during persistence would never fire and indicates a logic error.

Source

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

    /// <param name="callback">The callback to invoke when the application is being paused.</param>
    /// <returns>A subscription that can be used to unregister the callback when disposed.</returns>
    public PersistingComponentStateSubscription RegisterOnPersisting(Func<Task> callback)
        => RegisterOnPersisting(callback, null);

    /// <summary>
    /// Register a callback to persist the component state when the application is about to be paused.
    /// Registered callbacks can use this opportunity to persist their state so that it can be retrieved when the application resumes.
    /// </summary>
    /// <param name="callback">The callback to invoke when the application is being paused.</param>
    /// <param name="renderMode"></param>
    /// <returns>A subscription that can be used to unregister the callback when disposed.</returns>
    public PersistingComponentStateSubscription RegisterOnPersisting(Func<Task> callback, IComponentRenderMode? renderMode)
    {
        ArgumentNullException.ThrowIfNull(callback);

        if (PersistingState)
        {
            throw new InvalidOperationException("Registering a callback while persisting state is not allowed.");
        }

        var persistenceCallback = new PersistComponentStateRegistration(callback, renderMode);

        _registeredCallbacks.Add(persistenceCallback);

        return new PersistingComponentStateSubscription(_registeredCallbacks, persistenceCallback);
    }

    /// <summary>
    /// Register a callback to restore the state when the application state is being restored.
    /// </summary>
    /// <param name="callback"> The callback to invoke when the application state is being restored.</param>
    /// <param name="options">Options that control the restoration behavior.</param>
    /// <returns>A subscription that can be used to unregister the callback when disposed.</returns>
    public RestoringComponentStateSubscription RegisterOnRestoring(Action callback, RestoreOptions options)
    {
        Debug.Assert(CurrentContext != null);

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Register OnPersisting callbacks during OnInitialized (before persistence starts), not during it.
  2. Dispose the subscription when the component disposes to avoid duplicate registrations.
  3. Ensure components that need persistence are present before the persist phase begins.

Example fix

// before
protected override async Task OnPersistingAsync()
{
    State.RegisterOnPersisting(ExtraPersist); // inside persist phase -> throws
}

// after
protected override void OnInitialized()
{
    _sub = State.RegisterOnPersisting(Persist);
}
public void Dispose() => _sub?.Dispose();
Defensive patterns

Strategy: validation

Validate before calling

protected override void OnInitialized()
{
    // Only register before persistence; never inside an OnPersisting callback
    _persistSub = State.RegisterOnPersisting(PersistState);
}
private Task PersistState() { State.PersistAsJson("k", _data); return Task.CompletedTask; }

Try / catch

try { _sub = State.RegisterOnPersisting(cb); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Registering a callback while persisting"))
{ logger.LogWarning(ex, "Too late to register OnPersisting"); }

Prevention

When it happens

Trigger: Calling RegisterOnPersisting from inside another OnPersisting callback's execution; subscribing during the render that triggers persistence; late registration after the framework started serializing state.

Common situations: Components mounting late (during persistence) that try to register; dynamically added child components whose OnInitialized runs during the persist phase; re-registering on each render without disposing the prior subscription.

Related errors


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