dotnet/aspnetcore · error

Descriptors must be assigned a renderer ID before getting us

Error message

Descriptors must be assigned a renderer ID before getting used as root components

What it means

Thrown inside refreshRootComponents() in WebRootComponentManager.ts:288 when a root component descriptor has a pending operation (add/update/remove) but its assignedRendererId is undefined. Root components are the top-level interactive components discovered from SSR markers; each must be bound to a concrete renderer (Server or WebAssembly) before any operation can be dispatched to that renderer. The manager cannot route an operation to a runtime that was never assigned.

Source

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

  private refreshRootComponents(components: Iterable<RootComponentInfo>, discoverNewState = false) {
    const operationsByRendererId = new Map<WebRendererId, RootComponentOperation[]>();
    const rendererIds: Set<WebRendererId> = new Set<WebRendererId>();
    for (const component of components) {
      if (discoverNewState && component.assignedRendererId !== undefined) {
        // Capture the renderer IDs for the available components to determine the
        // effective render modes in the document for discovering new persisted state.
        rendererIds.add(component.assignedRendererId);
      }

      const operation = this.determinePendingOperation(component);
      if (!operation) {
        continue;
      }

      const rendererId = component.assignedRendererId;
      if (!rendererId) {
        throw new Error('Descriptors must be assigned a renderer ID before getting used as root components');
      }

      let operations = operationsByRendererId.get(rendererId);
      if (!operations) {
        operations = [];
        operationsByRendererId.set(rendererId, operations);
      }

      operations.push(operation);
    }

    let serverState = '';
    let webAssemblyState = '';
    if (discoverNewState) {
      for (const rendererId of rendererIds) {
        if (rendererId === WebRendererId.Server) {
          // We have server components. Try to discover the persisted state for them
          // and if there are no updates on this batch, push an empty set of operations

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Confirm you are not manually editing or injecting the component markers/blazor: component elements in the DOM that Blazor's SSR emits.
  2. Upgrade to the latest Blazor runtime — this invariant is enforced internally and bugs are fixed across releases.
  3. Reproduce minimally and file an issue with the rendered HTML at the moment of failure so the framework team can trace which descriptor lacked assignment.
  4. Avoid custom RootComponentManager/DescriptorHandler subclasses that bypass the assignedRendererId assignment step.
Defensive patterns

Strategy: validation

Type guard

function isRootComponentReady(c: RootComponentInfo): boolean {
  return c.assignedRendererId !== undefined && c.assignedRendererId !== null;
}

Prevention

When it happens

Trigger: An internal code path calls determinePendingOperation(component) which returns a non-null operation, then reads component.assignedRendererId and finds it falsy. This indicates the descriptor reached the operation-dispatch phase before being activated/assigned a renderer — a framework-internal invariant violation rather than a normal user-actionable flow.

Common situations: Almost always an internal Blazor framework bug or a corrupted component-marker state on the page (e.g., hand-edited blazor markers, partially rendered SSR output, or a race between descriptor discovery and renderer attachment). Rarely seen in correctly generated apps; investigate if custom rendering pipeline or third-party SSR manipulation is present.

Related errors


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