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

  1. Ensure the prerendered HTML and the interactive runtime come from the same app version — disable aggressive output caching that serves stale SSR markup.
  2. Avoid third-party scripts or DOM-diffing libraries that strip or relocate the blazor component markers.
  3. Reproduce with enhanced navigation/logging enabled to see if a navigation removed the component mid-update.
  4. 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

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


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