dotnet/aspnetcore · error · Error

End of component comment prerendered property must match…

Error message

End of component comment prerendered property must match the start comment prerender id: '${prerenderId}', '${prerenderEndId}'

What it means

Blazor pairs each prerendered component's start and end HTML comments via a shared 'prerenderId'. This error fires when an end marker's prerenderId does not equal the start marker's prerenderId for the same component region, meaning the comment stream is desynchronized. It is a guard against corrupt or reordered component boundaries produced during SSR or by intermediate HTML processing.

Solutions

  1. Disable HTML minification/comment-stripping middleware and any CDN HTML optimization on Blazor routes.
  2. Clear any partial/cached HTML fragments (output cache, response cache, CDN) and reproduce on a clean response.
  3. Confirm the Blazor JS bundle and server runtime are the same framework version.
  4. Inspect the rendered HTML around the failing component to verify start and end comments share one prerenderId.

Example fix

// before: cached fragment mixes component A's start marker with component B's end marker
// after: disable response caching/varied-by for prerendered component pages
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, VaryByHeader = "*")]
Defensive patterns

Strategy: validation

Validate before calling

// Pair start/end markers by prerenderId before processing
function markersMatch(start: { prerenderId?: string }, end: { prerenderId?: string }): boolean {
  return !!start.prerenderId && start.prerenderId === end.prerenderId;
}

Type guard

function hasMatchingPrerenderId(a: unknown, b: unknown, id: string): boolean {
  return !!a && !!b && (a as any).prerenderId === id && (b as any).prerenderId === id;
}

Prevention

When it happens

Trigger: Thrown at line 295 when prerenderEndId !== prerenderId, i.e. the end comment carries a different prerenderId than the start comment passed into validateEndComponentPayload. Happens when start/end comment pairs get split, reordered, or duplicated.

Common situations: An HTML minifier/rewriter that moves, duplicates, or merges comment nodes; a caching layer serving a stale fragment of one component inside another's boundaries; concurrent prerender of nested components producing interleaved markers that get reassembled incorrectly; version skew between server-side renderer and the JS that consumes markers.

Related errors


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

Appendix: source

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

  // Parameter definitions and values come Base64 encoded from the server, since they contain random data and can make the
  // comment invalid. We could unencode them in .NET Code, but that would be slower to do and we can leverage the fact that
  // JS provides a native function that will be much faster and that we are doing this work while we are fetching
  // blazor.boot.json
  payload.parameterDefinitions = payload.parameterDefinitions && atob(payload.parameterDefinitions);
  payload.parameterValues = payload.parameterValues && atob(payload.parameterValues);
}

function validateEndComponentPayload(json: string, prerenderId: string): void {
  const payload = JSON.parse(json) as ComponentEndMarker;
  if (Object.keys(payload).length !== 1) {
    throw new Error(`Invalid end of component comment: '${json}'`);
  }
  const prerenderEndId = payload.prerenderId;
  if (!prerenderEndId) {
    throw new Error(`End of component comment must have a value for the prerendered property: '${json}'`);
  }
  if (prerenderEndId !== prerenderId) {
    throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${prerenderId}', '${prerenderEndId}'`);
  }
}

class ComponentCommentIterator {
  private childNodes: NodeListOf<ChildNode>;

  private currentIndex: number;

  private length: number;

  public currentElement: ChildNode | undefined;

  public constructor(childNodes: NodeListOf<ChildNode>) {
    this.childNodes = childNodes;
    this.currentIndex = -1;
    this.length = childNodes.length;
  }

View on GitHub (pinned to 3600ca084e)