dotnet/aspnetcore · error · Error
End of component comment must have a value for the…
Error message
End of component comment must have a value for the prerendered property: '${json}' What it means
Blazor emits special HTML comments as start/end markers around prerendered components, and the end marker must be a JSON object containing exactly a 'prerenderId' field that pairs it with its start marker. This error is thrown by validateEndComponentPayload when the parsed end-marker JSON has no truthy 'prerenderId' property (e.g. it is missing, empty, null, or undefined). It indicates the SSR-rendered marker payload is malformed and Blazor cannot pair the component's prerender boundaries.
Solutions
- Disable any HTML minification/compression or response-rewriting middleware that alters comment contents and re-test.
- Verify the Blazor JS bundle version (blazor.web.js) exactly matches the server framework version producing the markers.
- Inspect the raw HTML response for the component end comment and confirm it contains a JSON payload like {"prerenderId":"<id>"}.
- If hosting behind a CDN/reverse proxy, bypass it to confirm it is not stripping comments or rewriting payloads.
Example fix
// before: end marker rendered as <!-- --> (empty) due to a minifier // after: preserve Blazor's markers verbatim in the response pipeline // e.g. disable HTML minification on the Blazor endpoint: services.AddHtmlMinification(o => o.MinifyComments = false); // or exclude Blazor routes from the minifying middleware.
Defensive patterns
Strategy: validation
Validate before calling
// Before relying on a prerendered component marker, validate its shape
function isValidEndMarker(json: string): boolean {
try {
const p = JSON.parse(json);
return p && typeof p.prerenderId === 'string' && p.prerenderId.length > 0;
} catch { return false; }
}
// if (!isValidEndMarker(payloadJson)) { /* surface a friendlier error / disable enhancement */ } Type guard
function isComponentEndMarker(v: unknown): v is { prerenderId: string } {
return typeof v === 'object' && v !== null
&& typeof (v as any).prerenderId === 'string'
&& (v as any).prerenderId.length > 0;
} Prevention
- Do not run HTML minification, compression rewriting, or CDN HTML optimization on Blazor endpoints.
- Keep the Blazor JS bundle and the server runtime on the exact same framework version.
- Avoid caching prerendered HTML fragments across requests.
- If you transform HTML responses, write a test that asserts Blazor comment markers survive round-tripping.
When it happens
Trigger: Triggered when ComponentDescriptorDiscovery parses an end-of-component comment whose JSON body lacks a prerenderId value (fails the !prerenderEndId check at line 293). Occurs after Object.keys(payload).length === 1 already passed, so the payload is a single-key object but that key is not a populated 'prerenderId'.
Common situations: A custom or hand-edited host page that strips/alters Blazor comment markers; an output-filtering middleware (HTML minifiers, response compressors, CDNs) that rewrites comment payloads; a mismatched Blazor server JS version vs. the blazor.web.js/blazor.server.js bundle; partial HTML caching that drops the prerenderId.
Related errors
- End of component comment prerendered property must match…
- Could not find an end component comment for
- Found malformed component comment at
- Root components cannot be marked as interactive. The
- assembly must be defined when using a descriptor.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/345cd9cce2fb78b4.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts:292
throw new Error('typeName must be defined when using a descriptor.');
}
// 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;View on GitHub (pinned to 3600ca084e)