dotnet/aspnetcore · error

No call dispatcher has been set.

Error message

No call dispatcher has been set.

What it means

Thrown by getDefaultCallDispatcher() in Microsoft.JSInterop.ts (line 255) when defaultCallDispatcher is still undefined — i.e., no .NET runtime has attached a call dispatcher yet. Static interop entry points like DotNet.invokeMethodAsync need a default dispatcher to route the call; without one there is no .NET runtime to talk to, so the call cannot proceed.

Source

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

  function parseJsonWithRevivers(callDispatcher: CallDispatcher, json: string | null): any {
      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,

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Wait for Blazor to be ready before invoking .NET — listen for the Blazor start promise or the appropriate 'blazor:ready'/DOMContentLoaded-after-boot event.
  2. For instance methods, use a captured DotNetObjectReference passed from .NET to JS instead of the static invokeMethod APIs.
  3. Confirm the Blazor runtime script (blazor.web.js / blazor.webassembly.js) is loaded and Blazor.start() has resolved.
  4. Ensure exactly the expected runtime attaches a dispatcher (see error 79 for the multi-runtime case).

Example fix

// before — runs before Blazor boots
DotNet.invokeMethodAsync('MyAssembly', 'DoThing'); // throws

// after
await Blazor.start();
DotNet.invokeMethodAsync('MyAssembly', 'DoThing');
Defensive patterns

Strategy: validation

Validate before calling

import { getDefaultCallDispatcher } ... ; // not exported; instead track readiness
let blazorReady = false;
Blazor.start().then(() => { blazorReady = true; });
function canInvokeStatic(): boolean { return blazorReady; }

Try / catch

try {
  await DotNet.invokeMethodAsync('Asm', 'Method');
} catch (e) {
  if (/No call dispatcher/i.test(e.message)) {
    await Blazor.start();
    await DotNet.invokeMethodAsync('Asm', 'Method');
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling DotNet.invokeMethod / invokeMethodAsync (the static, non-DotNetObject forms) before any .NET runtime has called attachDispatcher(). Common when JS interop runs before Blazor has booted the .NET runtime (e.g., a script running on page load before Blazor.start completes).

Common situations: Calling DotNet.invokeMethodAsync from a DOMContentLoaded handler or inline script before Blazor initializes. Standalone JS that uses Microsoft.JSInterop without a .NET host attached. Race where the JS bundle runs before the wasm/Server runtime registers its dispatcher. Using the wrong interop API in a multi-runtime scenario.

Related errors


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