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
- Match the Blazor JS bundle version to the runtime that produced the persisted state.
- Clear persisted state / cache and reload to regenerate it with matching versions.
- Avoid injecting custom renderer IDs into Blazor's root-component persisted state.
- 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
- Align Blazor JS bundle and runtime versions.
- Do not inject custom renderer IDs into persisted state.
- Clear persisted state / cache when upgrading Blazor versions.
- Test upgrades with a clean browser profile.
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
- Descriptors must be assigned a renderer ID before getting…
- Cannot merge mismatching component descriptors
- Could not resolve a root component with SSR component ID
- Dynamic root components have already been enabled.
- Dynamic root components have not been enabled in this…
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)