dotnet/aspnetcore · error · InvalidOperationException

Persisting state is only allowed during an OnPersisting call

Error message

Persisting state is only allowed during an OnPersisting callback.

What it means

PersistAsJson<T> (public generic) throws InvalidOperationException when PersistingState is false. State may only be written during an OnPersisting callback, which the framework gates by setting PersistingState=true; calling PersistAsJson from arbitrary code is rejected.

Source

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

        }

        return default;
    }

    /// <summary>
    /// Serializes <paramref name="instance"/> as JSON and persists it under the given <paramref name="key"/>.
    /// </summary>
    /// <typeparam name="TValue">The <paramref name="instance"/> type.</typeparam>
    /// <param name="key">The key to use to persist the state.</param>
    /// <param name="instance">The instance to persist.</param>
    [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")]
    public void PersistAsJson<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string key, TValue instance)
    {
        ArgumentNullException.ThrowIfNull(key);

        if (!PersistingState)
        {
            throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback.");
        }

        if (!_currentState.TryAdd(key, JsonSerializer.SerializeToUtf8Bytes(instance, JsonSerializerOptionsProvider.Options)))
        {
            throw new ArgumentException($"There is already a persisted object under the same key '{key}'");
        }
    }

    [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")]
    internal void PersistAsJson(string key, object instance, [DynamicallyAccessedMembers(JsonSerialized)] Type type)
    {
        ArgumentNullException.ThrowIfNull(key);

        if (!PersistingState)
        {
            throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback.");
        }

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Move the PersistAsJson call inside a method registered via RegisterOnPersisting (the OnPersisting callback).
  2. Verify the subscription is created in OnInitialized so the callback actually runs.
  3. Do not call PersistAsJson from constructors, render, or event handlers.

Example fix

// before
protected override void OnInitialized()
{
    State.PersistAsJson("cart", _cart); // outside persist phase -> throws
}

// after
protected override void OnInitialized()
{
    State.RegisterOnPersisting(() => { State.PersistAsJson("cart", _cart); return Task.CompletedTask; });
}
Defensive patterns

Strategy: validation

Validate before calling

protected override void OnInitialized()
{
    State.RegisterOnPersisting(Persist);
}
private Task Persist()
{
    // PersistingState is true here; safe to persist
    State.PersistAsJson("app.state", _data);
    return Task.CompletedTask;
}

Try / catch

try { State.PersistAsJson(key, value); }
catch (InvalidOperationException ex) when (ex.Message.Contains("only allowed during an OnPersisting callback"))
{ logger.LogError(ex, "PersistAsJson called outside persist phase"); }

Prevention

When it happens

Trigger: Calling State.PersistAsJson(...) from OnInitialized, OnAfterRender, an event handler, or any non-persisting context; calling it after the persist phase has ended.

Common situations: Developers assuming state can be persisted at any time; refactoring persistence logic out of the OnPersisting callback; timing issues where the callback registration did not run.

Related errors


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