dotnet/aspnetcore · error · Error

Either assemblyName or dotNetObjectId must have a non null v

Error message

Either assemblyName or dotNetObjectId must have a non null value.

What it means

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`.

Source

Thrown at src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts:277

  // exceptions also reach this, but via a different code path - see dotNetCriticalError below.
  console.error(line || '(null)');
  showErrorNotification();
};

function getArrayDataPointer<T>(array: System_Array<T>): number {
  if (isMonoRuntime) {
    return <number><any>array + 12;
  } else {
    return <number><any>array + 8;
  }
}

function attachInteropInvoker(): void {
  dispatcher = DotNet.attachDispatcher({
    beginInvokeDotNetFromJS: (callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: any | null, argsJson: string): void => {
      assertHeapIsNotLocked();
      if (!dotNetObjectId && !assemblyName) {
        throw new Error('Either assemblyName or dotNetObjectId must have a non null value.');
      }
      // As a current limitation, we can only pass 4 args. Fortunately we only need one of
      // 'assemblyName' or 'dotNetObjectId', so overload them in a single slot
      const assemblyNameOrDotNetObjectId: string = dotNetObjectId
        ? dotNetObjectId.toString()
        : assemblyName;

      Blazor._internal.dotNetExports!.BeginInvokeDotNet!(
        callId ? callId.toString() : null,
        assemblyNameOrDotNetObjectId,
        methodIdentifier,
        argsJson,
      );
    },
    endInvokeJSFromDotNet: (asyncHandle, succeeded, serializedArgs): void => {
      Blazor._internal.dotNetExports!.EndInvokeJS(serializedArgs);
    },
    sendByteArray: (id: number, data: Uint8Array): void => {

View on GitHub (pinned to 3600ca084e)

Solutions

  1. Always pass either a valid `assemblyName` for static `[JSInvokable]` methods or a live `DotNetObjectReference` for instance methods.
  2. Ensure `DotNetObjectReference` instances are not disposed before the JS side invokes them; track their lifetime explicitly.
  3. Validate that at least one identifier is non-null/non-empty before calling.
  4. If invoking via the dispatcher directly, mirror the high-level `invokeMethodAsync` argument contract.

Example fix

// before
await DotNet.invokeMethodAsync('', 'MyMethod'); // no assembly -> throw

// after
await DotNet.invokeMethodAsync('MyAssembly', 'MyMethod');
Defensive patterns

Strategy: validation

Validate before calling

function assertInteropTarget(assemblyName: string | null, dotNetObjectId: number | null) {
  if (!dotNetObjectId && !assemblyName) throw new Error('assemblyName or dotNetObjectId required');
}

Try / catch

try { await DotNet.invokeMethodAsync(asm, method); }
catch (e) { if (/assemblyName or dotNetObjectId/.test(e.message)) { /* fix call args */ } throw e; }

Prevention

When it happens

Trigger: Calling `DotNet.invokeMethodAsync`/`invokeMethod` (or the dispatcher's `beginInvokeDotNetFromJS`) with both `assemblyName` null/empty and `dotNetObjectId` null/0.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/b8c1d3176694edc4. Report an issue: GitHub.