dotnet/aspnetcore · error · InvalidOperationException

The registered callback {registration.Callback.Method.Name}

Error message

The registered callback {registration.Callback.Method.Name} must be associated with a component or define an explicit render mode type during registration.

What it means

Thrown by ComponentStatePersistenceManager.InferRenderModes when a registered persisting callback has RenderMode == null (no explicit render mode passed to RegisterOnPersisting) and its Callback.Target is not an IComponent, so the renderer cannot infer a render mode from an owning component. This matters only in multi-store (composite) scenarios - e.g. a Blazor Web App with InteractiveAuto - where the framework must route each callback to the correct store (Server vs WebAssembly).

Source

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

            }

            if (registration.Callback.Target is IComponent component)
            {
                var componentRenderMode = renderer.GetComponentRenderMode(component);
                if (componentRenderMode != null)
                {
                    _registeredCallbacks[i] = new PersistComponentStateRegistration(registration.Callback, componentRenderMode);
                }
                else
                {
                    // If we can't find a render mode, it's an SSR only component and we don't need to
                    // persist its state at all.
                    _registeredCallbacks[i] = default;
                }
                continue;
            }

            throw new InvalidOperationException(
                $"The registered callback {registration.Callback.Method.Name} must be associated with a component or define" +
                $" an explicit render mode type during registration.");
        }
    }

    internal Task<bool> TryPauseAsync(IPersistentComponentStateStore store)
    {
        List<Task<bool>>? pendingCallbackTasks = null;

        // We are iterating backwards to allow the callbacks to remove themselves from the list.
        // Otherwise, we would have to make a copy of the list to avoid running into situations
        // where we don't run all the callbacks because the count of the list changed while we
        // were iterating over it.
        // It is not allowed to register a callback while we are persisting the state, so we don't
        // need to worry about new callbacks being added to the list.
        for (var i = _registeredCallbacks.Count - 1; i >= 0; i--)
        {
            var registration = _registeredCallbacks[i];

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Pass an explicit render mode to RegisterOnPersisting, e.g. RegisterOnPersisting(callback, RenderMode.InteractiveWebAssembly) or InteractiveServer as appropriate.
  2. Register the callback from inside a component so its Target is the component and the render mode can be inferred.
  3. For persistent services, use [PersistentState] properties plus AddPersistentService<T> and specify the render mode at registration.
  4. If the callback genuinely belongs to no render mode, scope it to a single-store scenario instead of a composite store.

Example fix

// before (Auto render mode, service-based lambda)
_state.RegisterOnPersisting(() => PersistMyService());

// after
_state.RegisterOnPersisting(() => PersistMyService(), RenderMode.InteractiveWebAssembly);
Defensive patterns

Strategy: validation

Validate before calling

// When registering a persist callback from a non-component (service/lambda) target
// in a composite-store (Auto) app, always pass an explicit render mode:
PersistentComponentState RegisterPersist(PersistentComponentState state,
    Func<Task> callback, IComponentRenderMode mode)
    => state.RegisterOnPersisting(callback, mode) is var _
        ? throw null! // placeholder; real usage below
        : null!;

// Real usage:
// state.RegisterOnPersisting(PersistService, RenderMode.InteractiveWebAssembly);

Prevention

When it happens

Trigger: state.RegisterOnPersisting(callback) where callback is a lambda whose captured Target is a service or a static method (not a component), used in an app with multiple persistent state stores, without passing an explicit IComponentRenderMode.

Common situations: Persisting state from an injected service via a lambda (() => service.Persist()) in a Blazor Web App with global Auto interactivity; upgrading a Server-only app to Auto where an existing service-based callback now fails; registering a persist callback from a non-component host.

Related errors


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