dotnet/aspnetcore · error
Could not resolve a root component with SSR component ID '${
Error message
Could not resolve a root component with SSR component ID '${ssrComponentId}'. What it means
Thrown by WebRootComponentManager.resolveRootComponent(ssrComponentId) at line 479 when the given SSR component ID is not present in the _rootComponentsBySsrComponentId map. Each interactive root component is assigned a numeric SSR id during discovery; .NET calls back into this resolver when it needs the descriptor for a known root. A miss means .NET is asking about a component the JS side never registered (or already removed).
Source
Thrown at src/Components/Web.JS/src/Services/WebRootComponentManager.ts:479
// After the renderer attaches, we'll handle the removal of this descriptor again.
return null;
}
// Since the component will be getting completedly diposed from .NET (rather than replaced by another component, which can
// happen as a result of an 'update' operation), we indicate that its content should no longer be preserved on disposal.
setShouldPreserveContentOnInteractiveComponentDisposal(component.descriptor.start as unknown as LogicalElement, false);
// This component was removed from the document and we've assigned a renderer ID,
// so we'll dispose it in .NET.
component.hasPendingRemoveOperation = true;
return { type: 'remove', ssrComponentId: component.ssrComponentId };
}
}
public resolveRootComponent(ssrComponentId: number): ComponentDescriptor {
const component = this._rootComponentsBySsrComponentId.get(ssrComponentId);
if (!component) {
throw new Error(`Could not resolve a root component with SSR component ID '${ssrComponentId}'.`);
}
return component.descriptor;
}
public onAfterUpdateRootComponents(batchId: number): void {
const batch = this._pendingOperationBatches[batchId];
delete this._pendingOperationBatches[batchId];
for (const operation of batch.operations) {
switch (operation.type) {
case 'remove': {
// We can stop tracking this component now that .NET has acknowledged its removal.
const component = this._rootComponentsBySsrComponentId.get(operation.ssrComponentId);
if (component) {
this.unregisterComponent(component);
}
break;View on GitHub (pinned to 294cab2f9b)
Solutions
- Ensure the prerendered HTML and the interactive runtime come from the same app version — disable aggressive output caching that serves stale SSR markup.
- Avoid third-party scripts or DOM-diffing libraries that strip or relocate the blazor component markers.
- Reproduce with enhanced navigation/logging enabled to see if a navigation removed the component mid-update.
- Upgrade the runtime; resolution races have been addressed across Blazor releases.
Defensive patterns
Strategy: try-catch
Validate before calling
function hasRootComponent(manager: WebRootComponentManager, id: number): boolean {
// reflect on internal map via a guarded resolve
try { manager.resolveRootComponent(id); return true; } catch { return false; }
} Try / catch
try {
const descriptor = manager.resolveRootComponent(ssrId);
} catch (e) {
console.warn('Root component not resolvable, skipping update', ssrId, e);
} Prevention
- Do not serve stale prerendered HTML from cache alongside a fresh circuit.
- Prevent third-party scripts from removing blazor component markers.
- Avoid concurrent navigations that dispose roots mid-update.
- Keep prerender markup and runtime on the same version.
When it happens
Trigger: The .NET runtime invokes resolveRootComponent with an ssrComponentId that was never added, or that was already disposed/removed via onAfterUpdateRootComponents('remove'). Also possible if the DOM markers were stripped before resolution.
Common situations: SSR output and interactive runtime out of sync (stale prerendered page served from cache while a fresh circuit starts). DOM manipulation or aggressive content replacement that removes blazor component markers before the circuit references them. Concurrent navigation that disposes a root component while a queued update still references it. Version mismatch between prerendered HTML and runtime.
Related errors
- The '${beforeElementSelector}' selector is not supported.
- Could not find any element matching selector '${elementSelec
- Descriptors must be assigned a renderer ID before getting us
- Invalid sequence number or identifier '${sequenceOrIdentifie
- initialParameters must be an object, even if empty.
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/49e7e409e3ee353c.
Report an issue: GitHub.