dotnet/aspnetcore · critical · Error
No call dispatcher has been set.
Error message
No call dispatcher has been set.
What it means
Thrown by getDefaultCallDispatcher() when defaultCallDispatcher is still undefined, meaning DotNet.attachDispatcher() was never called. The default dispatcher is set by the Blazor runtime on startup; if it is missing, no .NET runtime is connected and any legacy DotNet.invokeMethod / invokeMethodAsync call has nowhere to route.
Solutions
- Wait for Blazor to be ready before calling: await Blazor.start(); or run code after the blazor:afterstarted event.
- If using the library standalone, call DotNet.attachDispatcher(yourDispatcher) first.
- Check the browser console for Blazor startup errors that prevented attachDispatcher.
Example fix
// before
DotNet.invokeMethodAsync('MyLib', 'DoThing'); // runs in <head>, Blazor not started
// after
document.addEventListener('blazor:afterstarted', () => {
DotNet.invokeMethodAsync('MyLib', 'DoThing');
}); Defensive patterns
Strategy: validation
Validate before calling
function isBlazorReady() { return typeof Blazor !== 'undefined' && Blazor._internal && defaultCallDispatcherSet; }
// or simpler: wait for the event
document.addEventListener('blazor:afterstarted', () => { /* safe to call DotNet.invokeMethodAsync */ }); Try / catch
try { return await DotNet.invokeMethodAsync('MyLib', 'Method'); } catch (e) { if (/No call dispatcher has been set/.test(e.message)) { console.warn('Blazor not started yet; retry after blazor:afterstarted.'); } throw e; } Prevention
- Gate DotNet.invokeMethod* calls behind the Blazor started event.
- If using the interop library standalone, attach a dispatcher explicitly.
- Check the console for Blazor startup errors before debugging interop failures.
When it happens
Trigger: Calling DotNet.invokeMethod or DotNet.invokeMethodAsync before Blazor has finished initializing (before Blazor.start() or before the auto-start script ran). Also when using the JS interop library standalone without attaching a dispatcher.
Common situations: A script in <head> or early <body> calling DotNet.invokeMethodAsync before Blazor is ready; using Microsoft.JSInterop.js outside of Blazor without a custom dispatcher; Blazor startup failed silently so attachDispatcher never ran.
Related errors
- There are multiple .NET runtimes present, so a default…
- Dynamic root components have not been enabled in this…
- For instance method calls, assemblyName should be null…
- JS object instance with ID
- The current dispatcher does not support synchronous calls…
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/4e5cdf7fca78a105.
Report an issue: GitHub.
Appendix: 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 3600ca084e)