{"record":{"id":"3e3fc6f62aee7131","repo":"dotnet/aspnetcore","slug":"found-malformed-component-comment-at-candidatest","errorCode":null,"errorMessage":"Found malformed component comment at ${candidateStart.textContent}","messagePattern":"Found malformed component comment at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts","lineNumber":161,"sourceCode":"        // Regardless of whether this comment matches the type we're looking for, we still need to move the iterator\n        // on to its end position since we don't want to recurse into unrelated prerendered components, nor do we want to get confused\n        // by the end marker.\n        const candidateEnd = getComponentEndComment(componentComment, candidateStart as Comment, commentNodeIterator);\n\n        if (type !== componentComment.type) {\n          return undefined;\n        }\n\n        switch (componentComment.type) {\n          case 'webassembly':\n            return createWebAssemblyComponentComment(componentComment, candidateStart as Comment, candidateEnd);\n          case 'server':\n            return createServerComponentComment(componentComment, candidateStart as Comment, candidateEnd);\n          case 'auto':\n            return createAutoComponentComment(componentComment, candidateStart as Comment, candidateEnd);\n        }\n      } catch (error) {\n        throw new Error(`Found malformed component comment at ${candidateStart.textContent}`);\n      }\n    } else {\n      return;\n    }\n  }\n}\n\nfunction parseCommentPayload(json: string): ServerComponentMarker | WebAssemblyComponentMarker | AutoComponentMarker {\n  const payload = JSON.parse(json);\n  const { type } = payload;\n  if (type !== 'server' && type !== 'webassembly' && type !== 'auto') {\n    throw new Error(`Invalid component type '${type}'.`);\n  }\n\n  return payload;\n}\n\nfunction assertNotDirectlyOnDocument(marker: Node) {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts#L143-L179","documentation":"A catch-all in getComponentComment (ComponentDescriptorDiscovery.ts:160) that re-wraps any exception thrown while parsing a Blazor component marker comment (the <!--Blazor:{...}--> HTML comments Blazor emits). It fires when the JSON payload of a component marker is structurally invalid so parseCommentPayload, getComponentEndComment, or the create*ComponentComment validators all throw. The wrapped message echoes the offending textContent so you can locate the corrupt marker in the DOM.","triggerScenarios":"Any of: invalid JSON in the marker; marker type not server/webassembly/auto; missing descriptor/sequence/assembly/typeName; missing or mismatched prerenderId end comment; marker placed directly under document (root component marked interactive). Each inner throw is caught and re-thrown as this message.","commonSituations":"A reverse proxy, CDN HTML minifier, or custom middleware strips/rewrites the Blazor marker comments; a custom Razor component serializer emits malformed descriptors; SSR output is cached or transformed; mixing incompatible Blazor server and client versions where the marker schema differs; browser extensions injecting comments that match the Blazor: regex.","solutions":["Inspect the raw server-rendered HTML (View Source, not DevTools tree) for the exact <!--Blazor:{...}--> comment and validate its JSON.","Disable HTML minification/rewriting (e.g. WebOptimizer, response compression that alters comments) for Blazor pages.","Ensure server (.NET) and client (blazor.web.js / blazor.webassembly.js) versions match exactly.","Reproduce with a minimal page and add a single component to isolate which marker is malformed."],"exampleFix":"// before (custom middleware mangling comments)\noutput = output.replace(/<!--.*?-->/g, '');\n\n// after\n// do not strip HTML comments on Blazor pages; preserve <!--Blazor:...--> markers verbatim","handlingStrategy":"validation","validationCode":"function looksLikeBlazorMarker(commentText: string): boolean {\n  const re = /^\\s*Blazor:[^{]*(?<descriptor>.*)$/;\n  const m = re.exec(commentText);\n  if (!m) return true; // not a Blazor marker, leave it alone\n  try { JSON.parse(m.groups!.descriptor); return true; } catch { return false; }\n}","typeGuard":"function isValidBlazorMarkerPayload(json: string): boolean {\n  try {\n    const p = JSON.parse(json);\n    return p && ['server','webassembly','auto'].includes(p.type);\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  discoverComponents(document, 'auto');\n} catch (e) {\n  if (/malformed component comment/.test((e as Error).message)) {\n    // surface a user-friendly error; disable enhanced nav and fall back to full page load\n    console.error('Blazor marker corruption detected:', e);\n    location.reload();\n  } else { throw e; }\n}","preventionTips":["Never strip or rewrite HTML comments on Blazor-served pages.","Keep server and client framework versions in lockstep.","Validate marker JSON in middleware tests if you must transform HTML."],"tags":["blazor","ssr","markers","html-comments","serialization","middleware"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}