dotnet/aspnetcore · error · InvalidOperationException

Render mode already set.

Error message

Render mode already set.

What it means

ComponentStatePersistenceManager.SetPlatformRenderMode sets the render mode used for platform-persisted service state through PersistentServicesRegistry. The registry's RenderMode can be assigned exactly once; calling SetPlatformRenderMode again after a non-null render mode is already stored throws. This prevents conflicting platform render-mode declarations when multiple render hosts (e.g., Server and WebAssembly) attempt to claim the same registry.

Solutions

  1. Call SetPlatformRenderMode at most once per manager; delegate platform render-mode selection to a single owning layer.
  2. If the mode is already set, skip the call; guard with a check of whether the registry already has a RenderMode if you have internal access.
  3. Reconcile conflicting render-mode configuration so only one platform render mode applies per services registry.

Example fix

// before: setting platform render mode twice
manager.SetPlatformRenderMode(RenderMode.InteractiveServer);
manager.SetPlatformRenderMode(RenderMode.InteractiveWebAssembly); // throws

// after: set once; ensure only the platform host assigns it
manager.SetPlatformRenderMode(RenderMode.InteractiveServer);
// do not call again; configure other endpoints to reuse the same manager
Defensive patterns

Strategy: validation

Validate before calling

// Only one platform host should set the render mode.
if (platformRenderMode == null) return;
try
{
    manager.SetPlatformRenderMode(platformRenderMode);
}
catch (InvalidOperationException) { /* already set; ensure consistency */ }

Try / catch

try { manager.SetPlatformRenderMode(renderMode); }
catch (InvalidOperationException ex) when (ex.Message == "Render mode already set.")
{
    // Log and verify the already-set mode matches expectation; otherwise reconcile config.
}

Prevention

When it happens

Trigger: Invoking SetPlatformRenderMode twice with different IComponentRenderMode values on the same manager. A hosting layer that registers the platform render mode during prerender and a second layer registers it again during interactive startup. Two endpoints with different configured render modes sharing the same services registration.

Common situations: Misconfigured Blazor Web App where both a prerendering endpoint and an interactive circuit attempt to set the platform render mode. Custom hosts that call SetPlatformRenderMode in startup and again per-request. Mixing InteractiveServer and InteractiveWebAssembly platform configuration on the same app instance.

Related errors


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

Appendix: source

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

            await store.PersistStateAsync(_currentState);
            return true;
        }
    }

    /// <summary>
    /// Initializes the render mode for state persisted by the platform.
    /// </summary>
    /// <param name="renderMode">The render mode to use for state persisted by the platform.</param>
    /// <exception cref="InvalidOperationException">when the render mode is already set.</exception>
    public void SetPlatformRenderMode(IComponentRenderMode renderMode)
    {
        if (_servicesRegistry == null)
        {
            return;
        }
        else if (_servicesRegistry?.RenderMode != null)
        {
            throw new InvalidOperationException("Render mode already set.");
        }

        _servicesRegistry!.RenderMode = renderMode;
    }

    private void InferRenderModes(Renderer renderer)
    {
        // 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];
            if (registration.RenderMode != null)
            {

View on GitHub (pinned to 3600ca084e)