{"record":{"id":"b8c1d3176694edc4","repo":"dotnet/aspnetcore","slug":"either-assemblyname-or-dotnetobjectid-must-have-a","errorCode":null,"errorMessage":"Either assemblyName or dotNetObjectId must have a non null value.","messagePattern":"Either assemblyName or dotNetObjectId must have a non null value\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts","lineNumber":277,"sourceCode":"  // exceptions also reach this, but via a different code path - see dotNetCriticalError below.\n  console.error(line || '(null)');\n  showErrorNotification();\n};\n\nfunction getArrayDataPointer<T>(array: System_Array<T>): number {\n  if (isMonoRuntime) {\n    return <number><any>array + 12;\n  } else {\n    return <number><any>array + 8;\n  }\n}\n\nfunction attachInteropInvoker(): void {\n  dispatcher = DotNet.attachDispatcher({\n    beginInvokeDotNetFromJS: (callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: any | null, argsJson: string): void => {\n      assertHeapIsNotLocked();\n      if (!dotNetObjectId && !assemblyName) {\n        throw new Error('Either assemblyName or dotNetObjectId must have a non null value.');\n      }\n      // As a current limitation, we can only pass 4 args. Fortunately we only need one of\n      // 'assemblyName' or 'dotNetObjectId', so overload them in a single slot\n      const assemblyNameOrDotNetObjectId: string = dotNetObjectId\n        ? dotNetObjectId.toString()\n        : assemblyName;\n\n      Blazor._internal.dotNetExports!.BeginInvokeDotNet!(\n        callId ? callId.toString() : null,\n        assemblyNameOrDotNetObjectId,\n        methodIdentifier,\n        argsJson,\n      );\n    },\n    endInvokeJSFromDotNet: (asyncHandle, succeeded, serializedArgs): void => {\n      Blazor._internal.dotNetExports!.EndInvokeJS(serializedArgs);\n    },\n    sendByteArray: (id: number, data: Uint8Array): void => {","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts#L259-L295","documentation":"Blazor's JS->.NET interop dispatches calls either to a static method (identified by `assemblyName` + `methodIdentifier`) or to an instance .NET object (identified by `dotNetObjectId`). The dispatcher needs at least one target; if both are null/empty the call has nowhere to go, so it throws before forwarding to `BeginInvokeDotNet`.","triggerScenarios":"Calling `DotNet.invokeMethodAsync`/`invokeMethod` (or the dispatcher's `beginInvokeDotNetFromJS`) with both `assemblyName` null/empty and `dotNetObjectId` null/0.","commonSituations":"A hand-built interop call omitting the assembly name; an object-reference-based call where the `DotNetObjectReference` was already disposed so its id is 0/null; generating `DotNet.invokeMethod` arguments dynamically and dropping both identifiers; misuse of low-level dispatcher APIs.","solutions":["Always pass either a valid `assemblyName` for static `[JSInvokable]` methods or a live `DotNetObjectReference` for instance methods.","Ensure `DotNetObjectReference` instances are not disposed before the JS side invokes them; track their lifetime explicitly.","Validate that at least one identifier is non-null/non-empty before calling.","If invoking via the dispatcher directly, mirror the high-level `invokeMethodAsync` argument contract."],"exampleFix":"// before\nawait DotNet.invokeMethodAsync('', 'MyMethod'); // no assembly -> throw\n\n// after\nawait DotNet.invokeMethodAsync('MyAssembly', 'MyMethod');","handlingStrategy":"validation","validationCode":"function assertInteropTarget(assemblyName: string | null, dotNetObjectId: number | null) {\n  if (!dotNetObjectId && !assemblyName) throw new Error('assemblyName or dotNetObjectId required');\n}","typeGuard":null,"tryCatchPattern":"try { await DotNet.invokeMethodAsync(asm, method); }\ncatch (e) { if (/assemblyName or dotNetObjectId/.test(e.message)) { /* fix call args */ } throw e; }","preventionTips":["Always pass a valid assemblyName for static [JSInvokable] methods.","Track DotNetObjectReference lifetime; do not invoke after dispose.","Validate at least one identifier is non-null before invoking.","Mirror invokeMethodAsync's argument contract when using the dispatcher."],"tags":["interop","jsinvokable","blazor","dispatcher","argument-validation"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}