dotnet/aspnetcore · error · Error

Cannot merge mismatching component descriptors

Error message

Cannot merge mismatching component descriptors:
${JSON.stringify(target)}
and
${JSON.stringify(source)}

What it means

Blazor can represent a single root component with multiple marker types (e.g. a 'server' marker and a 'webassembly' marker for an 'auto' component) that must be merged into one descriptor before rendering. mergeDescriptors refuses to combine two descriptors when canMergeDescriptors returns false, i.e. their types differ or their formattedComponentKey/locationHash-based keys do not match. The throw indicates two markers that purport to describe the same component are actually incompatible.

Solutions

  1. Force a full page reload (clear cached prerendered HTML) so start/end markers regenerate coherently.
  2. Ensure only one render mode (Server, WebAssembly, or Auto) is applied to a given component instance and not mixed across requests.
  3. Align client (blazor.web.js) and server framework versions.
  4. Inspect the two JSON markers printed in the error to see which type/key field diverges and trace where the stale marker originates.

Example fix

// before: component rendered with conflicting markers from two render modes
// after: declare a single render mode explicitly
@rendermode InteractiveAuto
// and avoid layering InteractiveServer on the same component elsewhere
Defensive patterns

Strategy: validation

Validate before calling

import { canMergeDescriptors } from './...';
// before calling mergeDescriptors:
if (!canMergeDescriptors(target, source)) {
  // log the mismatch and skip, or regenerate markers via full reload
  window.location.reload();
}

Type guard

function descriptorsMergeable(a: any, b: any): boolean {
  return a && b && a.type === b.type && doKeysMatch(a.key, b.key);
}

Try / catch

try {
  mergeDescriptors(target, source);
} catch (e) {
  console.error('Descriptor merge failed; reloading to regenerate markers', e);
  window.location.reload();
}

Prevention

When it happens

Trigger: Triggered at line 351 when canMergeDescriptors(target, source) is false — either target.type !== source.type, or doKeysMatch returns false comparing their key/formattedComponentKey/locationHash. This happens during descriptor discovery when iterating paired markers in the DOM.

Common situations: The DOM contains two component markers with the same DOM position but different component types (e.g. a leftover 'server' marker and a new 'webassembly' marker from an old/new render); mixed global+per-page interactivity on the same component; version skew between client and server marker formats; a stale fragment of prerendered HTML served alongside a fresh interactive render.

Related errors


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

Appendix: source

Thrown at src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts:351

  } as unknown as ComponentMarker;
}

function doKeysMatch(a: MarkerKey | undefined, b: MarkerKey | undefined) {
  if (!a || !b) {
    // Unspecified keys are never considered to be matching
    return false;
  }

  return a.locationHash === b.locationHash && a.formattedComponentKey === b.formattedComponentKey;
}

export function canMergeDescriptors(target: ComponentDescriptor, source: ComponentDescriptor): boolean {
  return target.type === source.type && doKeysMatch(target.key, source.key);
}

export function mergeDescriptors(target: ComponentDescriptor, source: ComponentDescriptor) {
  if (!canMergeDescriptors(target, source)) {
    throw new Error(`Cannot merge mismatching component descriptors:\n${JSON.stringify(target)}\nand\n${JSON.stringify(source)}`);
  }

  target.uniqueId = source.uniqueId;

  if (target.type === 'webassembly' || target.type === 'auto') {
    const sourceWebAssemblyData = source as WebAssemblyMarkerData;
    target.parameterDefinitions = sourceWebAssemblyData.parameterDefinitions;
    target.parameterValues = sourceWebAssemblyData.parameterValues;
  }

  if (target.type === 'server' || target.type === 'auto') {
    const sourceServerData = source as ServerMarkerData;
    target.sequence = sourceServerData.sequence;
    target.descriptor = sourceServerData.descriptor;
  }
}

export type ComponentDescriptor = ServerComponentDescriptor | WebAssemblyComponentDescriptor | AutoComponentDescriptor;

View on GitHub (pinned to 3600ca084e)