{"record":{"id":"a77960aede29a849","repo":"dotnet/aspnetcore","slug":"dynamic-root-components-have-already-been-enabled","errorCode":null,"errorMessage":"Dynamic root components have already been enabled.","messagePattern":"Dynamic root components have already been enabled\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/Components/Web.JS/src/Rendering/JSRootComponents.ts","lineNumber":129,"sourceCode":"\n      for (const jsEventCallbackWrapper of this._jsEventCallbackWrappers.values()) {\n        jsEventCallbackWrapper.dispose();\n      }\n    }\n  }\n}\n\n// Called by the framework\nexport function enableJSRootComponents(\n  rendererId: number,\n  managerInstance: DotNet.DotNetObject,\n  jsComponentParameters: JSComponentParametersByIdentifier,\n  jsComponentInitializers: JSComponentIdentifiersByInitializer\n): void {\n  if (manager && currentRendererId !== rendererId) {\n    // A different renderer type (e.g., Server vs WebAssembly) is trying to enable JS root components.\n    // This is a multi-host scenario which is not supported for dynamic root components.\n    throw new Error('Dynamic root components have already been enabled.');\n  }\n\n  // When the same renderer type re-enables (e.g., circuit restart or new circuit on same page),\n  // accept the new manager. The old manager's DotNetObjectReference is no longer valid anyway\n  // because the old circuit is gone. We don't dispose the old manager - doing so would cause\n  // JSDisconnectedException because the circuit that created it no longer exists.\n  currentRendererId = rendererId;\n  manager = managerInstance;\n  jsComponentParametersByIdentifier = jsComponentParameters;\n\n  if (!hasInitializedJsComponents) {\n    // Call the registered initializers. This is an arbitrary subset of the JS component types that are registered\n    // on the .NET side - just those of them that require some JS-side initialization (e.g., to register them\n    // as custom elements).\n    for (const [initializerIdentifier, componentIdentifiers] of Object.entries(jsComponentInitializers)) {\n      const initializerFunc = DotNet.findJSFunction(initializerIdentifier, 0) as JSComponentInitializerCallback;\n      for (const componentIdentifier of componentIdentifiers) {\n        const parameters = jsComponentParameters[componentIdentifier];","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Rendering/JSRootComponents.ts#L111-L147","documentation":"Thrown by enableJSRootComponents when a manager is already set and the new rendererId differs from currentRendererId. Blazor allows the same renderer type to re-enable (e.g. circuit restart) by replacing the manager, but a different renderer type (Server vs WebAssembly) trying to enable dynamic root components in one page is a multi-host scenario that is unsupported. The rendererId mismatch is the signal for a different host.","triggerScenarios":"Loading both Blazor Server and Blazor WebAssembly on the same page and both calling enableJSRootComponents; a hybrid hosting setup; a misconfigured boot config that starts two renderer types.","commonSituations":"Migrating an app from Server to WebAssembly and accidentally leaving both script tags; a portal/micro-frontend that embeds two Blazor apps sharing the global Blazor object.","solutions":["Use only one Blazor hosting model (Server OR WebAssembly) per page.","For micro-frontends, isolate each Blazor app in its own iframe or separate JS context.","If you genuinely need both, run them on separate pages/routes.","Check that blazor.boot.json and the script tag (blazor.server.js vs blazor.webassembly.js) match the intended single host."],"exampleFix":"// before: both loaded\n<script src=\"_framework/blazor.server.js\"></script>\n<script src=\"_framework/blazor.webassembly.js\"></script>\n\n// after: pick one\n<script src=\"_framework/blazor.webassembly.js\" autostart=\"false\"></script>","handlingStrategy":"validation","validationCode":"// Before starting, assert only one hosting model is present.\nconst scripts = document.querySelectorAll('script[src*=\"blazor.\"]');\nif (scripts.length > 1) {\n  throw new Error('Multiple Blazor hosting scripts detected on one page.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await Blazor.start();\n} catch (e) {\n  if (/already been enabled/.test(String(e?.message))) {\n    // already started by another host — surface a clear config error to the team\n  }\n}","preventionTips":["Load exactly one of blazor.server.js or blazor.webassembly.js per page.","For micro-frontends, isolate Blazor apps in iframes.","CI: assert the rendered HTML has only one Blazor boot script."],"tags":["blazor","root-components","multi-host","configuration"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}