{"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":null,"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/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Rendering/JSRootComponents.ts#L3-L39","documentation":"Thrown by RootComponentsFunctions.add when initialParameters is null or undefined. The type ComponentParameters = object | null | undefined permits those in the signature, but the runtime requires an actual object so the framework can iterate parameter keys. Passing nothing or null is treated as a programmer error, not an empty-parameter intent.","triggerScenarios":"Calling Blazor.rootComponents.add(element, 'MyComponent', null); calling with undefined because a variable was not initialized; forgetting the third argument.","commonSituations":"Dynamic root component registration where the component legitimately has no parameters and the developer passes null as 'empty'; reading parameters from a config source that returned null.","solutions":["Pass an empty object literal: {} when there are no parameters.","Default the variable: const params = config?.params ?? {}.","Add a type guard before calling add if parameters come from an untrusted source."],"exampleFix":"// before (throws)\nawait Blazor.rootComponents.add(hostEl, 'MyComponent', null);\n\n// after\nawait Blazor.rootComponents.add(hostEl, 'MyComponent', {});\n// or with defaults\nawait Blazor.rootComponents.add(hostEl, 'MyComponent', params ?? {});","handlingStrategy":"type-guard","validationCode":"const params = initialParameters ?? {};\nif (typeof params !== 'object' || params === null || Array.isArray(params)) {\n  throw new Error('initialParameters must be a plain object.');\n}\nawait Blazor.rootComponents.add(el, id, params);","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Default parameters to {} at the call site.","Type the parameter as Record<string, unknown> rather than allowing null.","Validate external config before passing it through."],"tags":["blazor","root-components","parameters","validation"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}