dotnet/aspnetcore · error · Error

Unexpected renderer ID

Error message

Unexpected renderer ID '${rendererId}' encountered while discovering new state.

What it means

While discovering persisted root-component state from the document, the manager iterates known renderer IDs (Server, WebAssembly). If it encounters a rendererId that is neither WebRendererId.Server (1) nor WebRendererId.WebAssembly (2), the discovery logic has no branch for it and throws. This guards against an unknown/out-of-range renderer ID appearing in persisted state.

Solutions

  1. Match the Blazor JS bundle version to the runtime that produced the persisted state.
  2. Clear persisted state / cache and reload to regenerate it with matching versions.
  3. Avoid injecting custom renderer IDs into Blazor's root-component persisted state.
  4. Upgrade or downgrade all Blazor packages (server + client + JS) to the same release.

Example fix

// before: client bundle v8 with persisted state from server v9 introducing a new renderer ID
// after: align versions
//   <script src="_framework/blazor.web.js" autostart="false"></script>
// matched to the same Microsoft.AspNetCore.Components.Web assembly version
Defensive patterns

Strategy: validation

Validate before calling

import { WebRendererId } from '../Rendering/WebRendererId';
function isKnownPersistedRenderer(id: number): boolean {
  return id === WebRendererId.Server || id === WebRendererId.WebAssembly;
}
// if (!isKnownPersistedRenderer(rendererId)) { /* version skew: reload with matching bundle */ }

Type guard

function isWebRendererId(v: unknown): v is number {
  return typeof v === 'number' && v >= 0 && v <= 4;
}

Try / catch

try {
  // discovery
} catch (e) {
  if (/Unexpected renderer ID/i.test((e as Error).message)) {
    // clear cache and reload with aligned versions
    window.location.reload();
  } else throw e;
}

Prevention

When it happens

Trigger: Thrown at line 328 inside the new-state discovery switch/if-chain when rendererId is not Server and not WebAssembly. The branch order implies only those two are expected for persisted interactive state.

Common situations: Version skew where a newer runtime emits a renderer ID the older JS bundle does not handle; custom interop injecting an unrecognized renderer ID; a stale blazor.boot.json or persisted state from a different framework version loaded by a newer/older client.

Related errors


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

Appendix: source

Thrown at src/Components/Web.JS/src/Services/WebRootComponentManager.ts:328

          // and if there are no updates on this batch, push an empty set of operations
          // to ensure we send the state over. Same for wasm below.
          serverState = discoverServerPersistedState(document) || '';
          if (serverState && serverState !== '') {
            const ops = operationsByRendererId.get(WebRendererId.Server);
            if (!ops) {
              operationsByRendererId.set(WebRendererId.Server, []);
            }
          }
        } else if (rendererId === WebRendererId.WebAssembly) {
          webAssemblyState = discoverWebAssemblyPersistedState(document) || '';
          if (webAssemblyState && webAssemblyState !== '') {
            const ops = operationsByRendererId.get(WebRendererId.WebAssembly);
            if (!ops) {
              operationsByRendererId.set(WebRendererId.WebAssembly, []);
            }
          }
        } else {
          throw new Error(`Unexpected renderer ID '${rendererId}' encountered while discovering new state.`);
        }
      }
    }

    for (const [rendererId, operations] of operationsByRendererId) {
      const batch: RootComponentOperationBatch = {
        batchId: this._nextOperationBatchId++,
        operations,
      };
      this._pendingOperationBatches[batch.batchId] = batch;
      const batchJson = JSON.stringify(batch);

      if (rendererId === WebRendererId.Server) {
        updateServerRootComponents(batchJson, serverState);
      } else {
        this.updateWebAssemblyRootComponents(batchJson, webAssemblyState);
      }
    }

View on GitHub (pinned to 3600ca084e)