{"record":{"id":"6323b286d6e537c7","repo":"dotnet/aspnetcore","slug":"end-of-component-comment-prerendered-property-must","errorCode":null,"errorMessage":"End of component comment prerendered property must match the start comment prerender id: '${prerenderId}', '${prerenderEndId}'","messagePattern":"End of component comment prerendered property must match the start comment prerender id: '(.+?)', '(.+?)'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts","lineNumber":295,"sourceCode":"  // Parameter definitions and values come Base64 encoded from the server, since they contain random data and can make the\n  // comment invalid. We could unencode them in .NET Code, but that would be slower to do and we can leverage the fact that\n  // JS provides a native function that will be much faster and that we are doing this work while we are fetching\n  // blazor.boot.json\n  payload.parameterDefinitions = payload.parameterDefinitions && atob(payload.parameterDefinitions);\n  payload.parameterValues = payload.parameterValues && atob(payload.parameterValues);\n}\n\nfunction validateEndComponentPayload(json: string, prerenderId: string): void {\n  const payload = JSON.parse(json) as ComponentEndMarker;\n  if (Object.keys(payload).length !== 1) {\n    throw new Error(`Invalid end of component comment: '${json}'`);\n  }\n  const prerenderEndId = payload.prerenderId;\n  if (!prerenderEndId) {\n    throw new Error(`End of component comment must have a value for the prerendered property: '${json}'`);\n  }\n  if (prerenderEndId !== prerenderId) {\n    throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${prerenderId}', '${prerenderEndId}'`);\n  }\n}\n\nclass ComponentCommentIterator {\n  private childNodes: NodeListOf<ChildNode>;\n\n  private currentIndex: number;\n\n  private length: number;\n\n  public currentElement: ChildNode | undefined;\n\n  public constructor(childNodes: NodeListOf<ChildNode>) {\n    this.childNodes = childNodes;\n    this.currentIndex = -1;\n    this.length = childNodes.length;\n  }\n","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts#L277-L313","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Disable HTML minification/comment-stripping middleware and any CDN HTML optimization on Blazor routes.","Clear any partial/cached HTML fragments (output cache, response cache, CDN) and reproduce on a clean response.","Confirm the Blazor JS bundle and server runtime are the same framework version.","Inspect the rendered HTML around the failing component to verify start and end comments share one prerenderId."],"exampleFix":"// before: cached fragment mixes component A's start marker with component B's end marker\n// after: disable response caching/varied-by for prerendered component pages\n[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, VaryByHeader = \"*\")]","handlingStrategy":"validation","validationCode":"// Pair start/end markers by prerenderId before processing\nfunction markersMatch(start: { prerenderId?: string }, end: { prerenderId?: string }): boolean {\n  return !!start.prerenderId && start.prerenderId === end.prerenderId;\n}","typeGuard":"function hasMatchingPrerenderId(a: unknown, b: unknown, id: string): boolean {\n  return !!a && !!b && (a as any).prerenderId === id && (b as any).prerenderId === id;\n}","tryCatchPattern":null,"preventionTips":["Disable HTML rewriting/minification that can reorder comment nodes.","Do not cache partial prerendered fragments.","Keep client and server Blazor versions aligned.","When building custom hosts, preserve all Blazor comment markers in document order."],"tags":["blazor","ssr","prerendering","component-markers","desync"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}