{"record":{"id":"f7abcc2f79e9766e","repo":"dotnet/aspnetcore","slug":"initialparameters-must-be-an-object-even-if-empty","errorCode":null,"errorMessage":"initialParameters must be an object, even if empty.","messagePattern":"initialParameters must be an object, even if empty\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Rendering/JSRootComponents.ts","lineNumber":21,"sourceCode":"\nimport { DotNet } from '@microsoft/dotnet-js-interop';\n\nconst pendingRootComponentContainerNamePrefix = '__bl-dynamic-root:';\nconst pendingRootComponentContainers = new Map<string, Element>();\nlet nextPendingDynamicRootComponentIdentifier = 0;\n\ntype ComponentParameters = object | null | undefined;\n\nlet manager: DotNet.DotNetObject | undefined;\nlet currentRendererId: number | undefined;\nlet jsComponentParametersByIdentifier: JSComponentParametersByIdentifier;\nlet hasInitializedJsComponents = false;\n\n// These are the public APIs at Blazor.rootComponents.*\nexport const RootComponentsFunctions = {\n  async add(toElement: Element, componentIdentifier: string, initialParameters: ComponentParameters): Promise<DynamicRootComponent> {\n    if (!initialParameters) {\n      throw new Error('initialParameters must be an object, even if empty.');\n    }\n\n    // Track the container so we can use it when the component gets attached to the document via a selector\n    const containerIdentifier = pendingRootComponentContainerNamePrefix + (++nextPendingDynamicRootComponentIdentifier).toString();\n    pendingRootComponentContainers.set(containerIdentifier, toElement);\n\n    // Instruct .NET to add and render the new root component\n    const componentId = await getRequiredManager().invokeMethodAsync<number>('AddRootComponent', componentIdentifier, containerIdentifier);\n    const component = new DynamicRootComponent(componentId, jsComponentParametersByIdentifier[componentIdentifier]);\n    await component.setParameters(initialParameters);\n    return component;\n  },\n};\n\nexport function getAndRemovePendingRootComponentContainer(containerIdentifier: string): Element | undefined {\n  const container = pendingRootComponentContainers.get(containerIdentifier);\n  if (container) {\n    pendingRootComponentContainers.delete(containerIdentifier);","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/Components/Web.JS/src/Rendering/JSRootComponents.ts#L3-L39","documentation":"Thrown by RootComponentsFunctions.add when initialParameters is falsy. The dynamic root component API requires a parameters object (possibly empty {}) so that setParameters always receives a defined object; null/undefined is rejected.","triggerScenarios":"Calling Blazor.rootComponents.add(element, identifier, null) or omitting the third argument.","commonSituations":"Adding a component that legitimately has no parameters and passing null instead of {}; optional-parameter handling that resolves to undefined.","solutions":["Pass an empty object {} when the component takes no parameters.","Default-coerce the argument: initialParameters ?? {} before calling add."],"exampleFix":"// before\nawait Blazor.rootComponents.add(el, 'MyComponent', null);\n\n// after\nawait Blazor.rootComponents.add(el, 'MyComponent', {});","handlingStrategy":"validation","validationCode":"// Coerce to a guaranteed object before add.\nconst params = initialParameters ?? {};\nawait Blazor.rootComponents.add(el, 'MyComponent', params);","typeGuard":"function isParametersObject(v: unknown): v is object {\n  return v !== null && typeof v === 'object';\n}","tryCatchPattern":null,"preventionTips":["Pass {} for components with no parameters.","Default-coerce with ?? {} at the call site.","Type the wrapper so null/undefined is rejected."],"tags":["blazor","root-components","validation"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}