dotnet/aspnetcore · error

There are multiple .NET runtimes present, so a default dispa

Error message

There are multiple .NET runtimes present, so a default dispatcher could not be resolved. Use DotNetObject to invoke .NET instance methods.

What it means

Thrown by getDefaultCallDispatcher() in Microsoft.JSInterop.ts (line 257) when defaultCallDispatcher is explicitly null — set that way by attachDispatcher() the moment a SECOND dispatcher is registered. With multiple .NET runtimes present, there is no safe 'default', so the static invokeMethod/invokeMethodAsync APIs refuse to guess. The error directs you to use DotNetObject (instance references) which carry their own dispatcher binding.

Source

Thrown at src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts:257

      currentCallDispatcher = callDispatcher;
      const result = json ? JSON.parse(json, (key, initialValue) => {
          // Invoke each reviver in order, passing the output from the previous reviver,
          // so that each one gets a chance to transform the value

          return jsonRevivers.reduce(
              (latestValue, reviver) => reviver(key, latestValue),
              initialValue
          );
      }) : null;
      currentCallDispatcher = undefined;
      return result;
  }

  function getDefaultCallDispatcher(): CallDispatcher {
      if (defaultCallDispatcher === undefined) {
          throw new Error("No call dispatcher has been set.");
      } else if (defaultCallDispatcher === null) {
          throw new Error("There are multiple .NET runtimes present, so a default dispatcher could not be resolved. Use DotNetObject to invoke .NET instance methods.");
      } else {
          return defaultCallDispatcher;
      }
  }

  interface PendingAsyncCall<T> {
    resolve: (value?: T | PromiseLike<T>) => void;
    reject: (reason?: any) => void;
  }

  /**
   * Represents the type of result expected from a JS interop call.
   */
  // eslint-disable-next-line no-shadow
  export enum JSCallResultType {
    Default = 0,
    JSObjectReference = 1,
    JSStreamReference = 2,

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Switch JS→.NET instance calls to use DotNetObjectReference: pass a [JSInvokable] object from .NET to JS and call invokeMethodAsync on that DotNetObject — it knows its own dispatcher.
  2. For static methods, route through a specific runtime's dispatcher object instead of the global default.
  3. If only one runtime is intended, ensure a second dispatcher is not being attached accidentally (double bootstrapping, stray script).
  4. Refactor interop so each call is bound to the runtime that owns the target object.

Example fix

// before — ambiguous with two runtimes
DotNet.invokeMethodAsync('MyAssembly', 'DoThing'); // throws

// after — instance-bound call
// C#: dotNetRef = DotNetObjectReference.Create(this); passed to JS
@code { [JSInvokable] public string DoThing() => "ok"; }
// JS:
dotNetRef.invokeMethodAsync('DoThing');
Defensive patterns

Strategy: type-guard

Type guard

function isDotNetObject(v: any): v is { invokeMethodAsync: (...args: any[]) => Promise<any> } {
  return v && typeof v.invokeMethodAsync === 'function' && typeof v['_id'] !== 'undefined';
}

Prevention

When it happens

Trigger: Two or more .NET runtimes call attachDispatcher() in the same page (e.g., a Blazor Server circuit and a Blazor WebAssembly runtime, or two wasm runtimes). After the second attaches, defaultCallDispatcher is nulled and any subsequent static invokeMethodAsync throws this message.

Common situations: Blazor Web App with both Server and WebAssembly interactive runtimes on the same page (InteractiveAuto during handoff, or custom multi-runtime hosting). Hosting two Blazor apps on one page. Custom runtime embedding that registers multiple dispatchers. Migrating from single to multi-runtime without switching JS interop calls to instance-based DotNetObjectReference.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/249f71f05c9f3188. Report an issue: GitHub.